home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
mxcode
/
soundss2
/
sbmod.asm
< prev
next >
Wrap
Assembly Source File
|
1994-08-24
|
86KB
|
3,402 lines
;
; ╔═══════════════════════════════════════════════╗
; ║ ║
TITLE ║ SOUNDBLASTER MODPLAYER (SB/SBPRO/16 & COMP.) ║
; ║ ║
; ╚═══════════════════════════════════════════════╝
;
; THIS MODULE PLAYER WAS CODED IN EARLY 1994
;
; BY SILVIO TURELLO (FRONTMAN/CREW242)
;
; IT IS A COMPLETELY REVISED VERSION OF THE MODLIB
;
; BY SERGE HUBER (NUMERUS/IMPHOBIA)
;
; PC-CONVERSION OF AMIGA PROTRACKER CODE (FOR SUPER PRO PLAY > MODLIB)
;
; BY JOSHUA C. JENSEN (CYBERSTRIKE/FORM. RENAISSANCE)
;
; ORIGIN SOURCE CODE CAME FROM THE PROTRACKER V2.1A
;
; BY LARS "ZAP" HAMRE (AMIGA FREELANCERS)
;
; FEATURES: 4/8-CHANNEL-MODS (M.K./FLT4/FLT8/8CHN), MIXINGRATE 10000-22222 HZ
;
; 1/2-FX-CHANNELS, FULL MOD-FX SUPPORT EXCEPT FILTER & REVERSE FUNK
;
; STEREO FOR SBPRO&16, ONLY 10% (APPROX.) PERFORMANCE REDUCTION
;
; 544 KB EMS-MEMORY SUPPORT FOR FX
;
DOSSEG
.MODEL TINY
STACKLENGTH = 0100H
ASSUME CS:DGROUP, DS:DGROUP; SS:DGROUP
;=============================================================================
MAX_CHAN_NUMB = 8
IRQ1 = 20H
IRQ2 = 21H
IRQ3 =0A0H
IRQ4 =0A1H
TIMER0 = 40H
PIT1 = 43H
UHR_INDEX = 70H
UHR_PORT = 71H
UHR_STATA = 0AH
UHR_STATB = 0BH
UHR_STATC = 0CH
;-----------------------------------------------------------------------------
.DATA
PROG_START_SEG DW 0
PROG_END_SEG DW 0
SYSTEM DB 0
IRQCOUNT DB 56
SBPRO_FLAG DB 0 ;0=SB, 1= SBPRO
SB_TYP DB 1 ;0=GUS, 1=SBPRO, 2=SB, 3=NO_SOUND
SB_MODUS DB 0 ;0=MUSIC&FX 1=MUSIC 2=FX 3=NO_SOUND
SAM_FLAG DB 0 ;FLIP SAMPLE CHANNEL
SAMPLE_RATE DW 22222
SBP_MIXERI DW 0224H
SBP_MIXERD DW 0225H
SB_RESET DW 0226H
SB_READ DW 022AH
SB_WRITE DW 022CH
SB_RSTAT DW 022EH
IRQ_NUMBER DB 7
DMA_CHANNEL DB 1
DMA_RATE DB 211
DMA_PAGE DB 0
DMA_POFF DW 0
DMA_DATA DB 87H,83H,81H,82H
MAINFREQ DD 41236
DMA_CX DW 434
DMA_MORE DW 434*4
DMA_MAX DW 434*16
DMA_NEWPTR DW 0
DMA_PTR DW 0
DMA_SEG DW 0
DMA_OFFSET DW 0
MIXMUL_OFFSET DW 0
COMP_SPEED2 DW 0106H
COMP_SPEED3 DW 4006H
COMP_SPEED4 DW 0
COMP_SPEED5 DW 1165
HOLD70 LABEL DWORD
DW OFFSET TIMER_HANDLER, 0
HOLDSB LABEL DWORD
DW OFFSET SBIRQ_HANDLER, 0
STEREO_PANNING DW 2,-2,0,2,0,-2,0,2,0
;-----------------------------------------------------------------------------
GDDLENGTH DD 0
GDDREST DW 0
GDDHANDLE DW 0
;GDDMIN DW -1
;GDDNEW DW 0
;GDDPOSL DW 0
;GDDPOSH DW 0
GDDZEIG DB 0
;-----------------------------------------------------------------------------
ERROR1_TEXT DB "MOD ERROR",13,10,"$"
ERROR2_TEXT DB "FX ERROR",13,10,"$"
ERROR3_TEXT DB "SB ERROR",13,10,"$"
OUT_TEXT DB "+/- VOLUME",13,10
DB "1 PLAY SAMPLE",13,10
DB "9 INIT REPEAT",13,10
DB "0 MUSIC MODE",13,10,"$"
SAMPLE_NAME DB "SAMPLE1.SAM",0
CONFIG_NAME DB "SS.CFG",0
VOL_NAME DB "FM.VOL",0
CFG_TEXT1 DB "DEFAULT MUSIC SYSTEM = ",0
CFG_TEXT2 DB "DEFAULT MUSIC MODE = ",0
CFG_TEXT3 DB "SOUNDCARD BASE PORT = ",0
CFG_TEXT4 DB "SOUNDCARD IRQ NUMBER = ",0
CFG_TEXT5 DB "SOUNDCARD DMA NUMBER = ",0
CFG_TEXT6 DB "DEFAULT SAMPLINGRATE = ",0
CFG_TEXT7 DB "SYSTEM COMPATIBILITY = ",0
;-----------------------------------------------------------------------------
; PLAYER - CONTROL STRUCTURES
;-----------------------------------------------------------------------------
ALIGN 2
MUSIC_VOL DB 255
MUSIC_VOLUME DB 255
FX_VOL DB 255
FX_VOLUME DB 255
MASTER_VOLUME DB 255
MOD_STAT DB 0
BPM_COUNT DB 1
BPM_SPEED DB 20
CH_NUMB DW 4
QUEUE_BUFFER LABEL WORD ;256 BYTES
NOTE DW MAX_CHAN_NUMB DUP(0)
CMD DB MAX_CHAN_NUMB DUP(0)
CMDLO DB MAX_CHAN_NUMB DUP(0)
START DW MAX_CHAN_NUMB DUP(0)
LENGTHI DW MAX_CHAN_NUMB DUP(0)
LOOPSTART DW MAX_CHAN_NUMB DUP(0)
REPLEN DW MAX_CHAN_NUMB DUP(0)
PERIOD DW MAX_CHAN_NUMB DUP(0)
FINETUNE DB MAX_CHAN_NUMB DUP(0)
VOLUME DB MAX_CHAN_NUMB DUP(0)
TONEPORTDIREC DB MAX_CHAN_NUMB DUP(1)
TONEPORTSPEED DB MAX_CHAN_NUMB DUP(0)
WANTEDPERIOD DW MAX_CHAN_NUMB DUP(0)
VIBRATOCMD DB MAX_CHAN_NUMB DUP(0)
VIBRATOPOS DB MAX_CHAN_NUMB DUP(0)
TREMOLOCMD DB MAX_CHAN_NUMB DUP(0)
TREMOLOPOS DB MAX_CHAN_NUMB DUP(0)
WAVECONTROL DB MAX_CHAN_NUMB DUP(0)
GLISSFUNK DB MAX_CHAN_NUMB DUP(0)
SAMPLEOFFSET DB MAX_CHAN_NUMB DUP(0)
PATTPOS DB MAX_CHAN_NUMB DUP(0)
LOOPCOUNT DB MAX_CHAN_NUMB DUP(0)
MFREQ DW MAX_CHAN_NUMB DUP(0)
MOFLOW DB MAX_CHAN_NUMB DUP(0)
SONG_DATA LABEL BYTE ;128 BYTES
MVOL DB MAX_CHAN_NUMB DUP(0)
MSEG DW MAX_CHAN_NUMB DUP(0)
MOFS DW MAX_CHAN_NUMB DUP(0)
MREPEAT DW MAX_CHAN_NUMB DUP(0)
MREPLEN DW MAX_CHAN_NUMB DUP(0)
MMAXREP DW MAX_CHAN_NUMB DUP(0)
MOD_NAME DB 40 DUP(0)
;-----------------------------------------------------------------------------
; Seg Stores the segment of the current sample. If
; the segment is zero, then there is no sample
; begin played.
; Offset The current offset of the sample being played
; in the segment specified.
; MaxRep The offset of the place in the sample to
; repeat from (i.e. the end of the sample).
; Freq The frequency to be played out the output
; device. It is actually a playback period,
; the upper byte determining the number of
; bytes to be added to the sample offset each
; interrupt, and the lower byte to be added
; over and over to generate carry flags to be
; added to additional offsets.
; Vol The actual volume to multiply the frequency
; by.
; Repeat The place to repeat from when the end of the
; loop is found.
; RepLen The number of bytes from Repeat to go to.
; When the end of this is found, it goes back
; to Repeat.
; OFlow The overflow variable used to generate the
; carry flags.
;-----------------------------------------------------------------------------
; PROTRACKER VARIABLES
;-----------------------------------------------------------------------------
ALIGN 2
MT_SPEED DB 6
MT_COUNTER DB 0
MT_PATTERNPOS DW 0
MT_SONGPOS DB 0
MT_PATTDELAYTIME2 DB 0
MT_PATTDELAYTIME DB 0
MT_PBREAKFLAG DB 0
MT_PBREAKPOS DB 0
MT_POSJUMPFLAG DB 0
MT_LOWMASK DB 0FFH
ALIGN 2
Effect_Jump_Table1 LABEL WORD
DW OFFSET Go_Arpeggio, OFFSET Go_PortaUp, OFFSET Go_PortaDown
DW OFFSET Go_TonePortamento, OFFSET Go_vibrato, OFFSET Go_TonePlusVolSlide
DW OFFSET Go_VibratoPlusVolSlide, OFFSET Go_Tremolo, OFFSET Go_Return
DW OFFSET Go_Return, OFFSET Go_VolumeSlide, OFFSET Go_Return, OFFSET Go_Return
DW OFFSET Go_Return, OFFSET Go_E_Commands, OFFSET Go_Return
Effect_Jump_Table2 LABEL WORD
DW OFFSET Go_PerNop, OFFSET Go_PerNop, OFFSET Go_PerNop
DW OFFSET Go_PerNop, OFFSET Go_PerNop, OFFSET Go_PerNop
DW OFFSET Go_PerNop, OFFSET Go_PerNop, OFFSET Go_PerNop
DW OFFSET Go_SampleOffset, OFFSET Go_PerNop, OFFSET Go_PositionJump
DW OFFSET Go_VolumeChange, OFFSET Go_PatternBreak
DW OFFSET Go_E_Commands, OFFSET Go_SetSpeed
Effect_Jump_Table3 LABEL WORD
DW OFFSET Go_Return, OFFSET Go_FinePortaUp, OFFSET Go_FinePortaDown
DW OFFSET Go_SetGlissControl, OFFSET Go_SetVibratoControl
DW OFFSET Go_SeTFineTune, OFFSET Go_JumpLoop, OFFSET Go_SetTremoloControl
DW OFFSET Go_Return, OFFSET Go_RetrigNote, OFFSET Go_VolumeFineUp
DW OFFSET Go_VolumeFineDown, OFFSET Go_NoteCut, OFFSET Go_NoteDelay
DW OFFSET Go_PatternDelay, OFFSET Go_Return
ALIGN 2
MT_VIBRATOTABLE DB 0, 24, 49, 74, 97,120,141,161
DB 180,197,212,224,235,244,250,253
DB 255,253,250,244,235,224,212,197
DB 180,161,141,120, 97, 74, 49, 24
ALIGN 2
MT_PERIODTABLE LABEL WORD
DW 856,808,762,720,678,640,604,570,538,508,480,453 ;C-1 to H-1 Finetune +0.
DW 428,404,381,360,339,320,302,285,269,254,240,226 ;C-2 to H-2 Finetune +0.
DW 214,202,190,180,170,160,151,143,135,127,120,113 ;C-3 to H-3 Finetune +0.
DW 107,101, 95, 90, 85, 80, 75, 71, 67, 63, 60, 56 ;C-4 to H-4 Finetune +0.
DW 850,802,757,715,674,637,601,567,535,505,477,450 ;C-1 to H-1 Finetune +1.
DW 425,401,379,357,337,318,300,284,268,253,239,225 ;C-2 to H-2 Finetune +1.
DW 213,201,189,179,169,159,150,142,134,126,119,113 ;C-3 to H-3 Finetune +1.
DW 106,100, 94, 89, 84, 79, 75, 71, 67, 83, 59, 56 ;C-4 to H-4 Finetune +1.
DW 844,796,752,709,670,632,597,563,532,502,474,447 ;C-1 to H-1 Finetune +2.
DW 422,398,376,355,335,316,298,282,266,251,237,224 ;C-2 to H-2 Finetune +2.
DW 211,199,188,177,167,158,149,141,133,125,118,112 ;C-3 to H-3 Finetune +2.
DW 105, 99, 94, 88, 83, 79, 74, 70, 66, 62, 59, 56 ;C-4 to H-4 Finetune +2.
DW 838,791,746,704,665,628,592,559,528,498,470,444 ;C-1 to H-1 Finetune +3.
DW 419,395,373,352,332,314,296,280,264,249,235,222 ;C-2 to H-2 Finetune +3.
DW 209,198,187,176,166,157,148,140,132,125,118,111 ;C-3 to H-3 Finetune +3.
DW 104, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59, 55 ;C-4 to H-4 Finetune +3.
DW 832,785,741,699,660,623,588,555,524,495,467,441 ;C-1 to H-1 Finetune +4.
DW 416,392,370,350,330,312,294,278,262,247,233,220 ;C-2 to H-2 Finetune +4.
DW 208,196,185,175,165,156,147,139,131,124,117,110 ;C-3 to H-3 Finetune +4.
DW 104, 98, 92, 87, 82, 78, 73, 69, 65, 62, 58, 55 ;C-4 to H-4 Finetune +4.
DW 826,779,736,694,655,619,584,551,520,491,463,437 ;C-1 to H-1 Finetune +5.
DW 413,390,368,347,328,309,292,276,260,245,232,219 ;C-2 to H-2 Finetune +5.
DW 206,195,184,174,164,155,146,138,130,123,116,109 ;C-3 to H-3 Finetune +5.
DW 103, 97, 92, 87, 82, 77, 73, 69, 65, 61, 58, 54 ;C-4 to H-4 Finetune +5.
DW 820,774,730,689,651,614,580,547,516,487,460,434 ;C-1 to H-1 Finetune +6.
DW 410,387,365,345,325,307,290,274,258,244,230,217 ;C-2 to H-2 Finetune +6.
DW 205,193,183,172,163,154,145,137,129,122,115,109 ;C-3 to H-3 Finetune +6.
DW 102, 96, 91, 86, 81, 77, 72, 68, 64, 61, 57, 54 ;C-4 to H-4 Finetune +6.
DW 814,768,725,684,646,610,575,543,513,484,457,431 ;C-1 to H-1 Finetune +7.
DW 407,384,363,342,323,305,288,272,256,242,228,216 ;C-2 to H-2 Finetune +7.
DW 204,192,181,171,161,152,144,136,128,121,114,108 ;C-3 to H-3 Finetune +7.
DW 102, 96, 90, 85, 80, 76, 72, 68, 64, 60, 57, 54 ;C-4 to H-4 Finetune +7.
DW 907,856,808,762,720,678,640,604,570,538,504,480 ;C-1 to H-1 Finetune -8.
DW 453,428,404,381,360,339,320,302,285,269,254,240 ;C-2 to H-2 Finetune -8.
DW 226,214,202,190,180,170,160,151,143,135,127,120 ;C-3 to H-3 Finetune -8.
DW 113,107,101, 95, 90, 85, 80, 75, 71, 67, 63, 60 ;C-4 to H-4 Finetune -8.
DW 900,850,802,757,715,675,636,601,567,535,505,477 ;C-1 to H-1 Finetune -7.
DW 450,425,401,379,357,337,318,300,284,268,253,238 ;C-2 to H-2 Finetune -7.
DW 225,212,200,189,179,169,159,150,142,134,126,119 ;C-3 to H-3 Finetune -7.
DW 112,106,100, 94, 89, 84, 79, 75, 71, 67, 63, 59 ;C-4 to H-4 Finetune -7.
DW 894,844,796,752,709,670,632,597,563,532,502,474 ;C-1 to H-1 Finetune -6.
DW 447,422,398,376,355,335,316,298,282,266,251,237 ;C-2 to H-2 Finetune -6.
DW 223,211,199,188,177,167,158,149,141,133,125,118 ;C-3 to H-3 Finetune -6.
DW 111,105, 99, 94, 88, 83, 79, 74, 70, 66, 62, 59 ;C-4 to H-4 Finetune -6.
DW 887,838,791,746,704,665,628,592,559,528,498,470 ;C-1 to H-1 Finetune -5.
DW 444,419,395,373,352,332,314,296,280,264,249,235 ;C-2 to H-2 Finetune -5.
DW 222,209,198,187,176,166,157,148,140,132,125,118 ;C-3 to H-3 Finetune -5.
DW 111,104, 99, 93, 88, 83, 78, 74, 70, 66, 62, 59 ;C-4 to H-4 Finetune -5.
DW 881,832,785,741,699,660,623,588,555,524,494,467 ;C-1 to H-1 Finetune -4.
DW 441,416,392,370,350,330,312,294,278,262,247,233 ;C-2 to H-2 Finetune -4.
DW 220,208,196,185,175,165,156,147,139,131,123,117 ;C-3 to H-3 Finetune -4.
DW 110,104, 98, 92, 87, 82, 78, 73, 69, 65, 61, 58 ;C-4 to H-4 Finetune -4.
DW 875,826,779,736,694,655,619,584,551,520,491,463 ;C-1 to H-1 Finetune -3.
DW 437,413,390,368,347,338,309,292,276,260,245,232 ;C-2 to H-2 Finetune -3.
DW 219,206,195,184,174,164,155,146,138,130,123,116 ;C-3 to H-3 Finetune -3.
DW 109,103, 97, 92, 87, 82, 77, 73, 69, 65, 61, 58 ;C-4 to H-4 Finetune -3.
DW 868,820,774,730,689,651,614,580,547,516,487,460 ;C-1 to H-1 Finetune -2.
DW 434,410,387,365,345,325,307,290,274,258,244,230 ;C-2 to H-2 Finetune -2.
DW 217,205,193,183,172,163,154,145,137,129,122,115 ;C-3 to H-3 Finetune -2.
DW 108,102, 96, 91, 86, 81, 77, 72, 68, 64, 61, 57 ;C-4 to H-4 Finetune -2.
DW 862,814,768,725,684,646,610,575,543,513,484,457 ;C-1 to H-1 Finetune -1.
DW 431,407,384,363,342,323,305,288,272,256,242,228 ;C-2 to H-2 Finetune -1.
DW 216,203,192,181,171,161,152,144,136,128,121,114 ;C-3 to H-3 Finetune -1.
DW 108,101, 96, 90, 85, 80, 76, 72, 68, 64, 60, 57 ;C-4 to H-4 Finetune -1.
;-----------------------------------------------------------------------------
; MTM - TRACK STRUCTURE
;-----------------------------------------------------------------------------
ALIGN 2
TRACK_NUMBER DW 0
TRACK_INFO DB 8 DUP(0)
TRACK_SEG DW 0
TRACKS_OFFSET DW OFFSET TRACK1_DATA, OFFSET TRACK2_DATA, OFFSET TRACK3_DATA
DW OFFSET TRACK4_DATA, OFFSET TRACK5_DATA, OFFSET TRACK6_DATA
DW OFFSET TRACK7_DATA, OFFSET TRACK8_DATA
TRACK1_DATA DB 128 DUP(0)
TRACK2_DATA DB 128 DUP(0)
TRACK3_DATA DB 128 DUP(0)
TRACK4_DATA DB 128 DUP(0)
TRACK5_DATA DB 128 DUP(0)
TRACK6_DATA DB 128 DUP(0)
TRACK7_DATA DB 128 DUP(0)
TRACK8_DATA DB 128 DUP(0)
;-----------------------------------------------------------------------------
; MOD - FILE STRUCTURE
;-----------------------------------------------------------------------------
ALIGN 2
SAMPLE_SIZE DW 0
PATTERN_SIZE DW 0
PATTERN_SEG DW 0
PATTERN_NUMBER DW 0
PATTERN_CURRENT DW 0
ISIZE DW 31 DUP(0)
IVOL_FINETUNE DW 31 DUP(0)
ILOOP_START DW 31 DUP(0)
ILOOP_SIZE DW 31 DUP(0)
SONG_SIZE DB 0
SONG_LOOP DB 0
MOD_SIGN DD 0
SAMPLE_SEG DW 31 DUP(0)
;-----------------------------------------------------------------------------
; SINGLE SAMPLE VARIABLES
;-----------------------------------------------------------------------------
SAMPLE_ZEIG DW 0
SAMPLE_MEM DW 48 DUP(0)
SAMPLE_BIG DW 48 DUP(0)
EMS_SEG DW 0
EMS_OFFSET DW 0
EMS_HANDLE DW 0
SAM_SEG1 DW 0
SAM_SEG2 DW 0
SAM_OFS1 DD 0
SAM_OFS2 DD 0
SAM_FRQ1 DW 0
SAM_FRQ2 DW 0
SAM_OFL1 DB 0
SAM_OFL2 DB 0
SAM_MAX1 DW 0
SAM_MAX2 DW 0
;-----------------------------------------------------------------------------
PROG_END LABEL BYTE ;THIS MUST BE THE LAST ROW IN DATA SEGMENT!
;-----------------------------------------------------------------------------
.386
.CODE
ORG 100H
;=============================================================================
MAIN PROC NEAR
JMP MAIN_CONT ;0100
RETF
CALL CONFIG_INIT ;0104
RETF
CALL LOAD_MOD ;0108
RETF
CALL PLAY_MUSIC ;010C
RETF
CALL STOP_MUSIC ;0110
RETF
CALL END_MUSIC ;0114
RETF
CALL LOAD_SAMPLE ;0118
RETF
CALL PLAY_SAMPLE ;011C
RETF
CALL END_SAMPLE ;0120
RETF
CALL SET_SAMPLERATE ;0124
RETF
CALL GET_VOLUME ;0128
RETF
CALL SET_VOLUME ;012C
RETF
CALL SET_SONGLOOP ;0130
RETF
CALL GET_SONGPOSITION ;0134
RETF
CALL SET_SONGPOSITION ;0138
RETF
CALL GET_SONGMOD ;013C
RETF
CALL SET_SONGMOD ;0140
RETF
CALL DSP_OFF ;0144
RETF
CALL DSP_RESET ;0148
RETF
CALL DSP_RESET ;014C/FREE
RETF
CALL DSP_RESET ;0150/FREE
RETF
MAIN ENDP
;=============================================================================
;
; MOD ROUTINES
;
;=============================================================================
; Format of a note: (ORIGINALLY MOD)
;
; 0 0 0 0 0 0 0 0
; | | | | | | | |
; / \ / / / \ \ \
; MSB of Ins. Note LSB Ins. Spec. Com. Data for special
;
; Format of a note: (MODIFIED BY LOADER)
;
; 0 0 0 0 0 0 0 0
; | | | | | | | |
; / / / \ \ \ \ \
; LSB NOTE SPEC.COM. MSB NOTE INSTRUMENT DATA FOR SPECIAL
;
;╒═══════════════════════════════════════════════════════════════════════════╕
;│ DESCRIPTION: This procedure shouldn't need to be called by anything else │
;│ but the interrupt (ever). It handles all note updating, │
;│ special effects, pointers, etc. │
;│ │
;│ *** This code came directly from the Amiga Protracker │
;│ playback code, written by Lars "Zap" Hamre. Give this │
;│ guy a pat on the back for such excellent code. │
;│ │
;│ BUGS : Or rather, non-implementations. Command EF - Funk it is │
;│ not all the way implemented. Command E0 - Filter is not │
;│ implemented at all. It is Amiga-specific, directly with │
;│ the Paula chip. │
;│ │
;│ IMPROVEMENTS: Optimized code for PC-architecure by the Frontman │
;│ │
;╘═══════════════════════════════════════════════════════════════════════════╛
CONTROL_CHANNELS PROC NEAR
CMP MOD_STAT,3
JAE Go_NoNewPosYet
INC MT_COUNTER
MOV AL,MT_COUNTER
CMP AL,MT_SPEED ; Does it match the current speed?
JB SHORT Go_NoNewNote ; No, just do fx.
MOV MT_COUNTER,0
CMP MT_PATTDELAYTIME2,0
JE SHORT Go_GetNewNote
CALL Go_CheckEfx ; Otherwise, just do fx only.
JMP Go_Dskip ; Then, jump to update block values.
Go_NoNewNote:
CALL Go_CheckEfx ; Do fx.
JMP Go_NoNewPosYet ; We don't update block values
; when we are just doing fx.
Go_GetNewNote:
MOV ES,TRACK_SEG
XOR SI,SI
Go_PlayVoice:
PUSH SI
MOV DI,SI
SHL DI,1
MOVZX BX,MT_SONGPOS ; BLOCK BEGINNING TO ES:BX
ADD BX,[TRACKS_OFFSET+DI]
MOVZX BX,[BX]
XCHG BL,BH ; MULTIPLY WITH 256 (TRACK-SIZE)
ADD BX,MT_PATTERNPOS
CMP [NOTE+DI],0 ; See if there is a note.
JNE SHORT Go_plvskip ; No, go on.
CMP [CMD+SI],0
JNE SHORT Go_plvskip
CMP [CMDLO+SI],0
JNE SHORT Go_plvskip
CALL Go_PerNop ; Otherwise, figure out the frequency.
Go_plvskip:
MOV AX,ES:[BX] ; Load this channel's information
MOV [NOTE+DI],AX ; from the actual track block and
AND [NOTE+DI],0FFFH
SHR AH,4 ; into our own internal structure
MOV [CMD+SI],AH ; to access it from.
MOV AX,ES:[BX+2]
MOV [CMDLO+SI],AH
OR AL,AL ; Instrument byte. Is there an ins?
JZ SHORT Go_SetRegs
DEC AL
MOVZX BX,AL ; GET SAMPLE SEGMENT
SHL BX,1
MOV AX,[SAMPLE_SEG+BX]
MOV [START+DI],AX ; Store it in our structure.
MOV AX,[ISIZE+BX] ; Get the length.
MOV [LENGTHI+DI],AX ; Store it appropriately.
MOV [REPLEN+DI],AX
MOV AX,[IVOL_FINETUNE+BX] ; Get the instrument volume and fine tune.
MOV [VOLUME+SI],AL ; Store it appropriately.
MOV [FINETUNE+SI],AH
MOV [MVOL+SI],AL
MOV CX,[ILOOP_SIZE+BX] ; Get Repeat length
CMP CX,8 ; If the length is less than four,
JB SHORT Go_NoLoop ; then there is no loop.
MOV AX,[ILOOP_START+BX] ; Get the Repeat start.
MOV [MREPEAT+DI],AX ; Move it into the appropriate
MOV [LOOPSTART+DI],AX ; structures and locations.
MOV [REPLEN+DI],CX ; Move the length into its storage.
ADD AX,CX ; We're figuring out the offset of
MOV [MREPLEN+DI],AX
MOV [MMAXREP+DI],AX ; where the repeat length breaks.
JMP SHORT Go_SetRegs ; Skip the no loop part.
Go_NoLoop:
XOR AX,AX ; Zero out AX.
MOV [MREPLEN+DI],AX ; Make sure that no loop exists and
MOV [MREPEAT+DI],AX
MOV [LOOPSTART+DI],AX
Go_SetRegs:
CMP [NOTE+DI],0 ; Grab the note.
JE Go_CheckMore ; Check the fx.
MOV AH,[CMD+SI] ; Get the special fx command.
MOV AL,[CMDLO+SI]
AND AL,0F0H ; Mask out the bits we don't want.
CMP AX,0E50H ; Is there a fine tune command
JE SHORT Go_DoSetFineTune
CMP AH,3 ; Is it a tone portamento?
JE SHORT Go_ChkTonePorta
CMP AH,5 ; Is it a tone and volume slide?
JE SHORT Go_ChkTonePorta
CMP AH,9 ; Is it a sample offset command?
JNE SHORT Go_SetPeriod ; If not, go do the actual note.
PUSH AX ; keep the command byte in mem
CALL Go_CheckMoreEfx
POP AX
JMP SHORT Go_SetPeriod ; Go do the actual note.
Go_ChkTonePorta:
CALL Go_SetTonePorta ; Do part of the tone portamento.
JMP Go_CheckMore ; Go do the fx.
Go_DoSetFineTune:
PUSH AX
CALL Go_SetFineTune ; Update the fine tune.
POP AX
;-----------------------------------------------------------------------------
; ──── We are now going to find the note in the period table. If it
; ──── doesn't exist, then no fine tuning can be performed. If it
; ──── does, and fine tuning is specified, then we can update it.
;-----------------------------------------------------------------------------
Go_SetPeriod:
MOV CL,AH
MOV AX,[NOTE+DI] ; Get the note.
DEC AX
MOV BX,OFFSET MT_PERIODTABLE; Set pointer to beginning of table.
CMP CL,3
JE SHORT Seek_Period
CMP CL,5
JNE SHORT Go_ftufound
Seek_Period:
INC AX
MOV CX,36+12 ; 36 periods to cycle through.
Go_ftuloop:
CMP AX,[BX] ; Check the note against the period.
JAE SHORT Go_Wizzyfound ; We found it!
INC BX
INC BX ; Otherwise, update the pointer and
LOOP Go_ftuloop ; keep looping.
JMP SHORT Go_Wizzyfound
Go_ftufound:
ADD BX,AX
Go_WizzyFound:
MOV AL,[FINETUNE+SI] ; Get the fine tune.
MOV CL,(36+12)*2 ; The period table's size is 72.
MUL CL ; Multiply it by the fine tune.
ADD BX,AX ; Add it onto the pointer to point to
MOV AX,[BX] ; the new period and get it.
MOV [PERIOD+DI],AX ; Store it.
MOV AH,[CMD+SI] ; Get the special fx command.
MOV AL,[CMDLO+SI]
AND AL,0F0H ; Mask out the bits we don't want.
CMP AX,0ED0H
JE SHORT Go_delnoc
MOV AL,[WAVECONTROL+SI] ; Get the WaveControl.
TEST AL,00000100B ; Amiga: BTST #2,WaveControl.
JZ SHORT Go_vibnoc ; If it is zero, then skip.
MOV [VIBRATOPOS+SI],0
Go_vibnoc:
TEST AL,01000000B ; Amiga: BTST #6,WaveControl
JZ SHORT Go_trenoc ; If it is zero, then skip.
MOV [TREMOLOPOS+SI],0 ; Zero the Tremolo offset.
Go_trenoc:
MOV AX,[START+DI] ; Get the start segment.
MOV [MSEG+DI],AX ; Store it to be updated.
MOV [MOFS+DI],0 ; Set the offset to zero.
MOV [MOFLOW+SI],0
MOV AX,[LENGTHI+DI]
MOV [MMAXREP+DI],AX ; Store in MaxRepeat
MOV CX,[PERIOD+DI]
CALL Go_PerNop2
JMP SHORT Go_CheckMore
Go_delnoc:
MOV AX,[MMAXREP+DI]
MOV [MOFS+DI],AX
Go_CheckMore:
CALL Go_CheckMoreEfx
Go_DoNext:
POP SI ;SKIP TO NEXT CHANNEL
INC SI
CMP SI,CH_NUMB
JB Go_PlayVoice
;-----------------------------------------------------------------------------
Go_Dskip:
ADD MT_PATTERNPOS,4 ; Increment position by one
MOV AL,MT_PATTDELAYTIME
OR AL,AL
JZ SHORT Go_dskc
MOV MT_PATTDELAYTIME2,AL
MOV MT_PATTDELAYTIME,0
Go_dskc:
CMP MT_PATTDELAYTIME2,0
JE SHORT Go_dska
DEC MT_PATTDELAYTIME2
JZ SHORT Go_dska
SUB MT_PATTERNPOS,4
Go_dska:
CMP MT_PBREAKFLAG,0
JE SHORT Go_nnpysk
MOV MT_PBREAKFLAG,0
MOVZX AX,MT_PBREAKPOS
MOV MT_PBREAKPOS,AH
SHL AX,2
MOV MT_PATTERNPOS,AX
Go_nnpysk:
CMP MT_PATTERNPOS,256
JB SHORT Go_NoNewPosYet
Go_NextPosition:
MOVZX AX,MT_PBREAKPOS
SHL AX,2
MOV MT_PATTERNPOS,AX
MOV MT_PBREAKPOS,0
MOV MT_POSJUMPFLAG,0
MOV AL,MT_SONGPOS
INC AL
AND AL,7FH
CMP AL,SONG_SIZE
JB SHORT Go_NoSongRestart
MOV AL,SONG_LOOP
CMP AL,SONG_SIZE
JB SHORT Go_NoSongRestart
MOV MOD_STAT,3
XOR AL,AL
Go_NoSongRestart:
MOV MT_SONGPOS,AL
Go_NoNewPosYet:
CMP MT_POSJUMPFLAG,0
JNE Go_NextPosition
RET
CONTROL_CHANNELS ENDP
;=============================================================================
Go_CheckEfx PROC NEAR
XOR SI,SI
Go_DoEfx:
PUSH SI
MOV DI,SI
SHL DI,1
MOV BL,[CMD+SI] ; Get the special command
MOV BH,[CMDLO+SI]
OR BX,BX
JNZ SHORT SetBack1
CALL Go_PerNop
JMP SHORT SetBack2
SetBack1:
XOR BH,BH
SHL BX,1
CALL [Effect_Jump_Table1+BX]
SetBack2:
POP SI
INC SI
CMP SI,CH_NUMB
JB Go_DoEfx
Go_Return:
RET
;-----------------------------------------------------------------------------
Go_PerNop PROC NEAR
MOV CX,[PERIOD+DI]
Go_PerNop2 PROC NEAR
XOR AX,AX
JCXZ SHORT Go_DivZero
MOV AX,WORD PTR MAINFREQ
MOV DX,WORD PTR MAINFREQ+2
DIV CX
Go_DivZero:
MOV [MFREQ+DI],AX
RET
Go_PerNop2 ENDP
Go_PerNop ENDP
Go_CheckEfx ENDP
;=============================================================================
;
; SI IS THE CHANNEL-BYTE-INDEX, DI IS THE CHANNEL-WORD-INDEX
;
;─────────────────────────────────────────────────────────────────────────────
; Effect 0 -- Arpeggio
;─────────────────────────────────────────────────────────────────────────────
Go_Arpeggio PROC NEAR
MOVZX AX,MT_COUNTER
MOV BL,3
MOV CX,[PERIOD+DI]
DIV BL
OR AH,AH
JZ SHORT Go_Arpeggio1
MOV AL,[CMDLO+SI]
CMP AH,2
JE SHORT Go_Arpeggio2
SHR AL,4
Go_Arpeggio2:
AND AL,0FH
SHL AL,1
MOVZX BX,AL
MOV AL,[FINETUNE+SI]
MOV DX,CX
MOV CL,(36+12)*2
MUL CL
PUSH DI
MOV DI,OFFSET MT_PERIODTABLE
ADD DI,AX
MOV AH,36+12
Go_arploop:
MOV CX,[BX+DI]
CMP DX,[DI]
JAE SHORT Go_arpafterloop
INC DI
INC DI
DEC AH
JNZ Go_arploop
POP DI
RET
Go_arpafterloop:
POP DI
Go_Arpeggio1:
CALL Go_PerNop2
RET
Go_Arpeggio ENDP
;─────────────────────────────────────────────────────────────────────────────
; Effect 1 -- Portamento Up
;─────────────────────────────────────────────────────────────────────────────
Go_PortaUp PROC NEAR
MOVZX AX,[CMDLO+SI] ; Number to slide up (was lo)
AND AL,MT_LOWMASK
MOV MT_LOWMASK,0FFH
SUB [PERIOD+DI],AX
MOV CX,[PERIOD+DI]
CMP CX,113
JGE SHORT Go_PortaUSkip
MOV CX,113
MOV [PERIOD+DI],CX
Go_PortaUSkip:
CALL Go_PerNop2
RET
Go_PortaUp ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect 2 -- Portamento Down
;────────────────────────────────────────────────────────────────────────────
Go_PortaDown PROC NEAR
MOVZX AX,[CMDLO+SI] ; Number to slide down
AND AL,MT_LOWMASK
MOV MT_LOWMASK,0FFH
ADD [PERIOD+DI],AX
MOV CX,[PERIOD+DI]
CMP CX,856
JL SHORT Go_PortaDSkip
MOV CX,856
MOV [PERIOD+DI],CX
Go_PortaDSkip:
CALL Go_PerNop2
RET
Go_PortaDown ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect 3 -- Tone Portamento
;────────────────────────────────────────────────────────────────────────────
Go_TonePortamento PROC NEAR
MOV AL,[CMDLO+SI]
OR AL,AL
JZ SHORT Go_TonePortNoChange
MOV [TONEPORTSPEED+SI],AL
MOV [CMDLO+SI],0
Go_TonePortNoChange:
CMP [WANTEDPERIOD+DI],0
JZ Go_Return
MOVZX DX,[TONEPORTSPEED+SI]
MOV CX,[PERIOD+DI]
MOV AX,[WANTEDPERIOD+DI]
CMP [TONEPORTDIREC+SI],0
JNE SHORT Go_TonePortaUp
Go_TonePortaDown:
ADD CX,DX
CMP AX,CX
JG SHORT Go_TonePortaSetPer
JMP SHORT Go_TonePortaEnd
Go_TonePortaUp:
SUB CX,DX
CMP AX,CX
JL SHORT Go_TonePortaSetPer
Go_TonePortaEnd:
MOV CX,AX
MOV [WANTEDPERIOD+DI],0
Go_TonePortaSetPer:
MOV [PERIOD+DI],CX
MOV AL,[GLISSFUNK+SI]
AND AL,0FH
JZ SHORT Go_GlissSkip
MOV AL,[FINETUNE+SI]
MOV BL,(36+12)*2
MUL BL
PUSH DI
MOV DI,OFFSET MT_PERIODTABLE
ADD DI,AX
XOR BX,BX
Go_GlissLoop:
CMP CX,[BX+DI]
JAE SHORT Go_GlissFound
INC BX
INC BX
CMP BX,(36+12)*2
JB Go_GlissLoop
MOV BX,(35+12)*2
Go_GlissFound:
MOV CX,[BX+DI]
POP DI
Go_GlissSkip:
CALL Go_PerNop2
RET
Go_TonePortamento ENDP
;-----------------------------------------------------------------------------
Go_SetTonePorta PROC NEAR
MOV DX,[NOTE+DI]
MOV AL,[FINETUNE+SI]
MOV CL,48*2 ;37
MUL CL
PUSH DI
MOV DI,OFFSET MT_PERIODTABLE
ADD DI,AX
XOR BX,BX
Go_StpLoop:
CMP DX,[BX+DI]
JAE SHORT Go_StpFound
INC BX
INC BX
CMP BX,48*2 ;37
JB Go_StpLoop
MOV BX,47*2 ;35
Go_StpFound:
MOV DL,[FINETUNE+SI]
AND DL,8
JZ SHORT Go_StpGoss
OR BX,BX
JZ SHORT Go_StpGoss
DEC BX
DEC BX
Go_StpGoss:
MOV DX,[BX+DI]
POP DI
MOV [WANTEDPERIOD+DI],DX
MOV AX,[PERIOD+DI]
MOV [TONEPORTDIREC+SI],0
CMP DX,AX
JE SHORT Go_ClearTonePorta
JA Go_Return
INC [TONEPORTDIREC+SI]
RET
Go_ClearTonePorta:
MOV [WANTEDPERIOD+DI],0
RET
Go_SetTonePorta ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect 4 -- Vibrato
;────────────────────────────────────────────────────────────────────────────
Go_Vibrato PROC NEAR
MOV AL,[CMDLO+SI]
OR AL,AL
JZ SHORT Go_Vibrato2
MOV BL,[VIBRATOCMD+SI]
AND AL,0FH
JZ SHORT Go_vibskip
AND BL,0F0H
OR BL,AL
Go_vibskip:
MOV AL,[CMDLO+SI]
AND AL,0F0H
JZ SHORT Go_vibskip2
AND BL,0FH
OR BL,AL
Go_vibskip2:
MOV [VIBRATOCMD+SI],BL
Go_Vibrato2:
MOV AL,[VIBRATOPOS+SI]
SHR AL,2
AND AX,1FH
MOV BL,[WAVECONTROL+SI]
AND BL,3
JZ SHORT Go_vib_sine
SHL AL,3
CMP BL,1
JE SHORT Go_vib_rampdown
MOV BL,255
JMP SHORT Go_vib_set
Go_vib_rampdown:
CMP [VIBRATOPOS+SI],0
JG SHORT Go_vib_rampdown2
MOV BL,AL
NOT BL
JMP SHORT Go_vib_set
Go_vib_rampdown2:
MOV BL,AL
JMP SHORT Go_vib_set
Go_vib_sine:
MOVZX BX,AL
MOV BL,[MT_VIBRATOTABLE+BX]
Go_vib_set:
MOV AL,[VIBRATOCMD+SI]
AND AL,0FH
MUL BL
SHR AX,7
MOV BX,AX
MOV AX,[PERIOD+DI]
CMP [VIBRATOPOS+SI],0
JG SHORT Go_VibratoNeg ; BMI
NEG BX
Go_VibratoNeg:
ADD AX,BX
Go_Vibrato3:
MOV CX,AX
CALL Go_PerNop2
MOV AL,[VIBRATOCMD+SI]
AND AL,0F0H
SHR AL,2
ADD [VIBRATOPOS+SI],AL
RET
Go_Vibrato ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect 5 -- Tone and Volume Slide
;────────────────────────────────────────────────────────────────────────────
Go_TonePlusVolSlide PROC NEAR
CALL Go_TonePortNoChange
JMP Go_VolumeSlide
Go_TonePlusVolSlide ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect 6 -- Vibrato and Volume Slide
;────────────────────────────────────────────────────────────────────────────
Go_VibratoPlusVolSlide PROC NEAR
CALL Go_Vibrato2 ;was mt_vibrato2
JMP Go_VolumeSlide
Go_VibratoPlusVolSlide ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect 7 -- Tremolo
;────────────────────────────────────────────────────────────────────────────
Go_Tremolo PROC NEAR
MOV AL,[CMDLO+SI]
OR AL,AL
JZ SHORT Go_Tremolo2
MOV BL,[TREMOLOCMD+SI]
AND AL,0FH
JZ SHORT Go_treskip
AND BL,0F0H
OR BL,AL
Go_treskip:
MOV AL,[CMDLO+SI]
AND AL,0F0H
JZ SHORT Go_treskip2
AND BL,0FH
OR BL,AL
Go_treskip2:
MOV [TREMOLOCMD+SI],BL
Go_Tremolo2:
MOV AL,[TREMOLOPOS+SI]
SHR AL,2
AND AX,1FH
MOV BL,[WAVECONTROL+SI]
SHR BL,4
AND BL,3
JZ SHORT Go_tre_sine
SHL AL,3
CMP BL,1
JE SHORT Go_tre_rampdown
MOV BL,255
JMP SHORT Go_tre_set
Go_tre_rampdown:
CMP [TREMOLOPOS+SI],0
JG SHORT Go_tre_rampdown2
MOV BL,AL
NOT BL
JMP SHORT Go_tre_set
Go_tre_rampdown2:
MOV BL,AL
JMP SHORT Go_tre_set
Go_tre_sine:
MOVZX BX,AL
MOV BL,[MT_VIBRATOTABLE+BX]
Go_tre_set:
MOV AL,[TREMOLOCMD+SI]
AND AL,0FH
MUL BL
MOV BX,AX
SHR BX,6
MOV AL,[VOLUME+SI]
CMP [TREMOLOPOS+SI],0
JG SHORT Go_TremoloNeg ; BMI jns
ADD AL,BL
JMP SHORT Go_Tremolo3
Go_TremoloNeg:
SUB AL,BL
Go_Tremolo3:
JNC SHORT Go_TremoloSkip
XOR AX,AX
Go_TremoloSkip:
CMP AL,40H
JBE SHORT Go_TremoloOK ; BLS
MOV AL,40H
Go_TremoloOK:
MOV [MVOL+SI],AL ;was ah
MOV AL,[TREMOLOCMD+SI]
AND AL,0F0H
SHR AL,2
ADD [TREMOLOPOS+SI],AL
RET
Go_Tremolo ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect 9 -- Sample Offset
;────────────────────────────────────────────────────────────────────────────
Go_SampleOffset PROC NEAR
MOVZX AX,[CMDLO+SI]
OR AL,AL
JZ SHORT Go_sononew
MOV [SAMPLEOFFSET+SI],AL
Go_sononew:
MOVZX AX,[SAMPLEOFFSET+SI]
XCHG AL,AH
CMP AX,[MMAXREP+DI]
JAE SHORT Go_sofskip
MOV [MOFS+DI],AX
RET
Go_sofskip:
MOV AX,[MOFS+DI]
MOV [MMAXREP+DI],AX
RET
Go_SampleOffset ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect A -- Volume Slide
;────────────────────────────────────────────────────────────────────────────
Go_VolumeSlide PROC NEAR
MOV AL,[CMDLO+SI]
SHR AL,4
OR AL,AL
JZ SHORT Go_VolSlideDown
Go_VolSlideUp:
ADD [VOLUME+SI],AL
CMP [VOLUME+SI],40H
JBE SHORT Go_vsdskip
MOV [VOLUME+SI],40H
JMP SHORT Go_vsdskip
Go_VolSlideDown:
MOV AL,[CMDLO+SI]
AND AL,0FH
Go_VolSlideDown2:
SUB [VOLUME+SI],AL
JNC SHORT Go_vsdskip
MOV [VOLUME+SI],0
Go_vsdskip:
MOV AL,[VOLUME+SI]
MOV [MVOL+SI],AL ;was ah
RET
Go_VolumeSlide ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect B -- Position Jump
;────────────────────────────────────────────────────────────────────────────
Go_PositionJump PROC NEAR
MOV AL,[CMDLO+SI] ; Get where to jump
DEC AL ; Update the
MOV MT_SONGPOS,AL ; information.
Go_pj2: MOV MT_PBREAKPOS,0
MOV MT_POSJUMPFLAG,1
RET
Go_PositionJump ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect C -- Volume Change
;────────────────────────────────────────────────────────────────────────────
Go_VolumeChange PROC NEAR
MOV AL,[CMDLO+SI] ; Get value for volume
CMP AL,40H ; Is it greater than 40h?
JBE SHORT Go_VolumeOK ; Nope
MOV AL,40H
Go_VolumeOK:
MOV [VOLUME+SI],AL
MOV [MVOL+SI],AL ;was ah
RET
Go_VolumeChange ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect D -- Pattern Break
;────────────────────────────────────────────────────────────────────────────
Go_PatternBreak PROC NEAR
MOV AL,[CMDLO+SI] ; Break to where?
MOV BL,AL ; POSITION IS IN DECIMAL
SHR AL,4
MOV CL,10
MUL CL
AND BL,0FH
ADD AL,BL
CMP AL,63
JG Go_pj2
MOV MT_PBREAKPOS,AL
MOV MT_POSJUMPFLAG,1
RET
Go_PatternBreak ENDP
;────────────────────────────────────────────────────────────────────────────
; Effect F -- Set Speed
;────────────────────────────────────────────────────────────────────────────
Go_SetSpeed PROC NEAR
MOV AL,[CMDLO+SI] ; Get value for speed
OR AL,AL
JZ Go_Return
CMP AL,32 ; Is it a BPM value ?
JAE SHORT Go_SetBPM
MOV MT_COUNTER,0
MOV MT_SPEED,AL
RET
Go_SetBPM:
MOVZX CX,AL
ADD CX,100
MOV AX,4500
XOR DX,DX
DIV CX ; NORMAL BPM SPEED IS 125
MOV BPM_SPEED,AL
RET
Go_SetSpeed ENDP
;=============================================================================
Go_CheckMoreEfx PROC NEAR
MOVZX BX,[CMD+SI]
SHL BL,1
JMP [Effect_Jump_Table2+BX]
Go_CheckMoreEfx ENDP
;─────────────────────────────────────────────────────────────────────────────
; Effect E
;─────────────────────────────────────────────────────────────────────────────
Go_E_Commands PROC NEAR
MOVZX BX,[CMDLO+SI]
SHR BL,4
SHL BL,1
MOV AL,[CMDLO+SI]
AND AL,0FH
JMP [Effect_Jump_Table3+BX]
;-----------------------------------------------------------------------------
; Effect 0 -- FilterOnOff
; Effect 1 -- Fine Porta Up
;-----------------------------------------------------------------------------
Go_FinePortaUp:
CMP MT_COUNTER,0
JNE Go_Return
MOV MT_LOWMASK,0FH
JMP Go_PortaUp
;-----------------------------------------------------------------------------
; Effect 2 -- Fine Porta Down
;-----------------------------------------------------------------------------
Go_FinePortaDown:
CMP MT_COUNTER,0
JNE Go_Return
MOV MT_LOWMASK,0FH
JMP Go_PortaDown
;-----------------------------------------------------------------------------
; Effect 3 -- Set Gliss Control
;-----------------------------------------------------------------------------
Go_SetGlissControl:
AND [GLISSFUNK+SI],0F0H
OR [GLISSFUNK+SI],AL
RET
;-----------------------------------------------------------------------------
; Effect 4 -- Set Vibrato Control
;-----------------------------------------------------------------------------
Go_SetVibratoControl:
AND [WAVECONTROL+SI],0F0H
OR [WAVECONTROL+SI],AL
RET
;-----------------------------------------------------------------------------
; Effect 5 -- Set Fine Tune
;-----------------------------------------------------------------------------
Go_SetFineTune:
MOV [FINETUNE+SI],AL
RET
;-----------------------------------------------------------------------------
; Effect 6 -- Jump Loop
;-----------------------------------------------------------------------------
Go_JumpLoop:
CMP MT_COUNTER,0
JNE Go_Return
OR AL,AL
JZ SHORT Go_SetLoop
CMP [LOOPCOUNT+SI],0
JE SHORT Go_jumpcnt
DEC [LOOPCOUNT+SI]
JZ Go_Return
Go_jmploop:
MOV AL,[PATTPOS+SI]
MOV MT_PBREAKPOS,AL
MOV MT_PBREAKFLAG,1
RET
Go_jumpcnt:
MOV [LOOPCOUNT+SI],AL
JMP Go_jmploop
Go_SetLoop:
MOV AX,MT_PATTERNPOS
SHR AX,2
MOV [PATTPOS+SI],AL
RET
;-----------------------------------------------------------------------------
; Effect 7 -- Set Tremolo Control
;-----------------------------------------------------------------------------
Go_SetTremoloControl:
SHL AL,4
AND [WAVECONTROL+SI],0FH
OR [WAVECONTROL+SI],AL
RET
;-----------------------------------------------------------------------------
; Effect 9 -- Retrig Note
;-----------------------------------------------------------------------------
Go_RetrigNote:
MOV BL,AL
OR BL,BL
JZ SHORT Go_rtnend
MOVZX AX,MT_COUNTER
OR AL,AL
JNZ SHORT Go_rtnskp
CMP [NOTE+DI],0
JNE SHORT Go_rtnskp
MOV MT_COUNTER,0
Go_rtnskp:
DIV BL
XCHG AL,AH
OR AL,AL
JNZ SHORT Go_rtnend
Go_DoRetrig:
XOR AX,AX
MOV [MOFS+DI],AX
MOV [MOFLOW+SI],AL
MOV AX,[LENGTHI+DI]
MOV [MMAXREP+DI],AX
MOV AX,[LOOPSTART+DI]
MOV [MREPEAT+DI],AX
ADD AX,[REPLEN+DI]
MOV [MREPLEN+DI],AX
Go_rtnend:
RET
;-----------------------------------------------------------------------------
; Effect A -- Volume Fine Up
;-----------------------------------------------------------------------------
Go_VolumeFineUp:
CMP MT_COUNTER,0
JNE Go_Return
JMP Go_VolSlideUp
;-----------------------------------------------------------------------------
; Effect B -- Volume Fine Down
;-----------------------------------------------------------------------------
Go_VolumeFineDown:
CMP MT_COUNTER,0
JNZ Go_Return
JMP Go_VolSlideDown2
;-----------------------------------------------------------------------------
; Effect C -- Note Cut
;-----------------------------------------------------------------------------
Go_NoteCut:
CMP AL,MT_COUNTER
JNE Go_Return
MOV [VOLUME+SI],0
MOV [MVOL+SI],AL
RET
;-----------------------------------------------------------------------------
; Effect D -- Note Delay
;-----------------------------------------------------------------------------
Go_NoteDelay:
CMP AL,MT_COUNTER
JNE Go_Return
CMP [NOTE+DI],0
JE Go_Return
JMP Go_DoRetrig
;-----------------------------------------------------------------------------
; Effect E -- Pattern Delay
;-----------------------------------------------------------------------------
Go_PatternDelay:
CMP MT_COUNTER,0
JNE Go_Return
CMP MT_PATTDELAYTIME2,0
JNZ Go_Return
INC AL
MOV MT_PATTDELAYTIME,AL
RET
;-----------------------------------------------------------------------------
; Effect F -- Funk It
;-----------------------------------------------------------------------------
Go_E_Commands ENDP
;=============================================================================
;=============================================================================
MIXUP_CHANNELS PROC NEAR
CMP MOD_STAT,3 ;MOD HAS FINISHED
JB SHORT GMX7
XOR AX,AX
XOR BX,BX
MOV CX,MAX_CHAN_NUMB
LMX4: MOV [MSEG+BX],AX
INC BX
INC BX
LOOP LMX4
GMX7: MOV CX,DMA_CX
CMP MOD_STAT,2
JB SHORT GMX3
OUT 0CH,AL ;DMA FLIP-FLOP RESET
MOVZX DX,DMA_CHANNEL
SHL DL,1
IN AL,DX ;GET DMA POSITION
XCHG AL,AH
IN AL,DX
XCHG AL,AH
SUB AX,DMA_POFF ;AX IS OFFSET OF DMA-POS
AND AX,0FFFEH
ADD AX,DMA_MORE
CMP AX,DMA_MAX
JB SHORT GMX1
SUB AX,DMA_MAX
GMX1: ADD AX,DMA_OFFSET
SUB AX,DMA_PTR
JNC SHORT GMX2
ADD AX,DMA_MAX
GMX2: MOV CX,AX ;CX IS NUMBER OF BYTES TO CALCULATE
ADD AX,DMA_PTR
SUB AX,DMA_OFFSET
SUB AX,2000H
JC SHORT GMX3
SUB CX,AX
GMX3: XOR BX,BX
XOR DI,DI
CLD
SHR CX,1
JZ SHORT GMX5
TEST SBPRO_FLAG,1
JNZ SHORT SBPRO_MIXING ;SB MONO-MIXING
CALL MIX_CHANNELA ;INIT BUFFER WITH FIRST CHANNEL
INC BX
INC DI
INC DI
DEC CH_NUMB
LMX1: CALL MIX_CHANNELM ;MIX CHANNELS
INC BX
INC DI
INC DI
CMP BX,CH_NUMB
JB LMX1
INC CH_NUMB
CALL MIX_CHANNELX ;END UP BUFFER WITH LAST CHANNEL
CALL MIX_CHANNELS ;SAMPLE1: MIDDLE CHANNEL
LMX2: MOV AX,DMA_NEWPTR ;CHECK DMA-BUFFER OVERFLOW
MOV SI,DMA_MAX ;ITS A RING BUFFER
ADD SI,DMA_OFFSET
CMP AX,SI
JB SHORT GMX4
MOV CX,AX
MOV DI,DMA_OFFSET
SUB CX,SI
JZ SHORT GMX6
CLD
SHR CX,1
PUSH DS
MOV ES,DMA_SEG
MOV DS,DMA_SEG
REP MOVSW ;MOVE OVERFLOW DATA
POP DS
GMX6: MOV AX,DI
GMX4: MOV DMA_PTR,AX ;START DSP OUTPUT
CMP MOD_STAT,1
JNE SHORT GMX5
INC MOD_STAT
CALL DSP_OUT
GMX5: RET
SBPRO_MIXING:
SHR CX,1 ;SBPRO STEREO-MIXING
JZ GMX5
MOV AX,[STEREO_PANNING+DI]
ADD DMA_PTR,AX
CALL MIX_CHANNELA ;INIT BUFFER WITH FIRST CHANNEL
INC BX
INC DI
INC DI
MOV AX,[STEREO_PANNING+DI]
ADD DMA_PTR,AX
CALL MIX_CHANNELA ;THIS CHANNEL SHOULD BE ON RIGHT
INC BX ;(OTHERWISE DMA_NEWPTR IS 2 TOO HIGH)
INC DI
INC DI
LMX3: MOV AX,[STEREO_PANNING+DI]
ADD DMA_PTR,AX
CALL MIX_CHANNELM ;MIX CHANNELS
INC BX
INC DI
INC DI
CMP BX,CH_NUMB
JB LMX3
MOV AX,[STEREO_PANNING+DI]
ADD DMA_PTR,AX
PUSH CX
CALL MIX_CHANNELS1 ;SAMPLE1: LEFT CHANNEL
POP CX
SUB DMA_PTR,2
PUSH CX
CALL MIX_CHANNELS2 ;SAMPLE2: RIGHT CHANNEL
POP CX
CALL FIXUP_CHANNELS ;END UP CHANNEL BY CONVERTING IT TO PC
JMP LMX2
MIXUP_CHANNELS ENDP
;-----------------------------------------------------------------------------
MIX_CHANNELA PROC NEAR
PUSH CX BX DI
MOV SI,[MOFS+DI] ;GET OFFSET IN SAMPLE (START ALWAYS= 0)
MOV DX,[MFREQ+DI] ;OFF ADD AFTER FIXED POINT (X/256)
MOVZX BP,DH ;OFF ADD BEFORE FIXED POINT
MOV DH,[MOFLOW+BX] ;ACTUAL AFTER FIXED POINT VALUE
MOV AL,[MVOL+BX] ;SAMPLE VOLUME
MOV BL,MUSIC_VOL ;MASTER VOLUME
MUL BL
XOR AL,AL
ADD AX,MIXMUL_OFFSET
MOV BX,AX ;DS:BX POINTS INTO MIXMUL_VOLUMETABLE
MOV ES,[MSEG+DI]
MOV AX,[MMAXREP+DI] ;SP IS OFFSET OF SAMPLE ENDING
MOV WORD PTR SELFM1+2,AX
MOV WORD PTR SELFM2+2,AX
MOV AL,2
CLEAR_PENT_PREF1:
DEC AL ;CLEAR ALSO PENTIUM PQ!!
JNZ CLEAR_PENT_PREF1
JMP SHORT CLEAR_PREF1 ;CLEAR PREFETCH QUEUE!!!
CLEAR_PREF1:
MOV DI,DMA_PTR
MOV DS,DMA_SEG ;DS:DI IS DMA-BUFFER POINTER
MOV AX,ES ;ES:SI IS SAMPLE-DATA POINTER
OR AX,AX
JZ SHORT SILENT_FILLA
MIX_LOOPA:
SELFM1: CMP SI,0FFFFH ;SELFMODIFYING CODE
JAE SHORT REST_FILLA
MOV AH,ES:[SI]
ADD DH,DL
ADC SI,BP
SELFM2: CMP SI,0FFFFH ;SELFMODIFYING CODE
JAE SHORT REST_FILLA
MOV AL,ES:[SI]
ADD DH,DL
ADC SI,BP
XLATB
XCHG AL,AH
XLATB
MOV [DI],AX
SELFN1: ADD DI,2 ;SELFMODIFYING CODE
LOOP MIX_LOOPA
MIX_ENDA:
MOV AX,CS
MOV DS,AX
MOV AX,ES
MOV DMA_NEWPTR,DI
POP DI BX CX
MOV [MSEG+DI],AX
MOV [MOFLOW+BX],DH
MOV [MOFS+DI],SI
RET
REST_FILLA:
POP SI
PUSH SI
MOV AX,CS:[MREPLEN+SI]
OR AX,AX
JZ SHORT SILENT_FILLA
MOV CS:[MMAXREP+SI],AX
MOV WORD PTR CS:SELFM1+2,AX
MOV WORD PTR CS:SELFM2+2,AX
MOV SI,CS:[MREPEAT+SI]
JMP MIX_LOOPA
SILENT_FILLA:
MOV BX,DS
MOV ES,BX
TEST CS:SBPRO_FLAG,1
JNZ SHORT SILENT_FILLB
REP STOSW
MOV ES,AX
JMP MIX_ENDA
SILENT_FILLB:
STOSW
INC DI
INC DI
LOOP SILENT_FILLB
MOV ES,AX
JMP MIX_ENDA
MIX_CHANNELA ENDP
;-----------------------------------------------------------------------------
MIX_CHANNELM PROC NEAR
PUSH CX BX DI
MOV SI,[MOFS+DI] ;GET OFFSET IN SAMPLE (START ALWAYS= 0)
MOV DX,[MFREQ+DI] ;OFF ADD AFTER FIXED POINT (X/256)
MOVZX BP,DH ;OFF ADD BEFORE FIXED POINT
MOV DH,[MOFLOW+BX] ;ACTUAL AFTER FIXED POINT VALUE
MOV AL,[MVOL+BX] ;SAMPLE VOLUME
MOV BL,MUSIC_VOL ;MASTER VOLUME
MUL BL
XOR AL,AL
ADD AX,MIXMUL_OFFSET
MOV BX,AX ;DS:BX POINTS INTO MIXMUL_VOLUMETABLE
MOV ES,[MSEG+DI]
MOV AX,[MMAXREP+DI] ;OFFSET OF SAMPLE ENDING
MOV WORD PTR SELFM3+2,AX
MOV WORD PTR SELFM4+2,AX
MOV AL,2
CLEAR_PENT_PREF2:
DEC AL ;CLEAR ALSO PENTIUM PQ!!
JNZ CLEAR_PENT_PREF2
JMP SHORT CLEAR_PREF2 ;CLEAR PREFETCH QUEUE!!!
CLEAR_PREF2:
MOV DI,DMA_PTR
MOV DS,DMA_SEG ;DS:DI IS DMA-BUFFER POINTER
MOV AX,ES
OR AX,AX
JZ SHORT MIX_ENDM
MIX_LOOPM:
SELFM3: CMP SI,0FFFFH ;SELFMODIFYING CODE
JAE SHORT REST_FILLM
MOV AH,ES:[SI]
ADD DH,DL
ADC SI,BP
SELFM4: CMP SI,0FFFFH ;SELFMODIFYING CODE
JAE SHORT REST_FILLM
MOV AL,ES:[SI]
ADD DH,DL
ADC SI,BP
XLATB
XCHG AL,AH
XLATB
ADD [DI],AX
SELFN2: ADD DI,2 ;SELFMODIFYING CODE
LOOP MIX_LOOPM
MIX_ENDM:
MOV AX,CS
MOV DS,AX
MOV AX,ES
POP DI BX CX
MOV [MSEG+DI],AX
MOV [MOFLOW+BX],DH
MOV [MOFS+DI],SI
RET
REST_FILLM:
POP SI
PUSH SI
MOV AX,CS:[MREPLEN+SI]
OR AX,AX
JZ SHORT SILENT_FILLM
MOV CS:[MMAXREP+SI],AX
MOV WORD PTR CS:SELFM3+2,AX
MOV WORD PTR CS:SELFM4+2,AX
MOV SI,CS:[MREPEAT+SI]
JMP MIX_LOOPM
SILENT_FILLM:
MOV ES,AX
JMP MIX_ENDM
MIX_CHANNELM ENDP
;-----------------------------------------------------------------------------
MIX_CHANNELX PROC NEAR
PUSH CX BX DI
MOV SI,[MOFS+DI] ;GET OFFSET IN SAMPLE (START ALWAYS= 0)
MOV DX,[MFREQ+DI] ;OFF ADD AFTER FIXED POINT (X/256)
MOVZX BP,DH ;OFF ADD BEFORE FIXED POINT
MOV DH,[MOFLOW+BX] ;ACTUAL AFTER FIXED POINT VALUE
MOV AL,[MVOL+BX] ;SAMPLE VOLUME
MOV BL,MUSIC_VOL ;MASTER VOLUME
MUL BL
XOR AL,AL
ADD AX,MIXMUL_OFFSET
MOV BX,AX ;DS:BX POINTS INTO MIXMUL_VOLUMETABLE
MOV ES,[MSEG+DI]
MOV AX,[MMAXREP+DI] ;SP IS OFFSET OF SAMPLE ENDING
MOV WORD PTR SELFM5+2,AX
MOV WORD PTR SELFM6+2,AX
MOV WORD PTR SELFM7+2,8080H
CMP SAM_SEG1,0
JE SHORT NO_SAMPLES
MOV WORD PTR SELFM7+2,0
NO_SAMPLES:
MOV AL,2
CLEAR_PENT_PREF3:
DEC AL ;CLEAR ALSO PENTIUM PQ!!
JNZ CLEAR_PENT_PREF3
JMP SHORT CLEAR_PREF3 ;CLEAR PREFETCH QUEUE!!!
CLEAR_PREF3:
MOV DI,DMA_PTR
MOV DS,DMA_SEG ;DS:DI IS DMA-BUFFER POINTER
MOV AX,ES ;ES:SI IS SAMPLE-DATA POINTER
OR AX,AX
JZ SHORT SILENT_FILLX
MIX_LOOPX:
SELFM5: CMP SI,0FFFFH ;SELFMODIFYING CODE
JAE SHORT REST_FILLX
MOV AH,ES:[SI]
ADD DH,DL
ADC SI,BP
SELFM6: CMP SI,0FFFFH ;SELFMODIFYING CODE
JAE SHORT REST_FILLX
MOV AL,ES:[SI]
ADD DH,DL
ADC SI,BP
XLATB
XCHG AL,AH
XLATB
ADD [DI],AX
SELFM7: XOR WORD PTR [DI],8080H
INC DI
INC DI
LOOP MIX_LOOPX
MIX_ENDX:
MOV AX,CS
MOV DS,AX
MOV AX,ES
POP DI BX CX
MOV [MSEG+DI],AX
MOV [MOFLOW+BX],DH
MOV [MOFS+DI],SI
RET
REST_FILLX:
POP SI
PUSH SI
MOV AX,CS:[MREPLEN+SI]
OR AX,AX
JZ SHORT SILENT_FILLX
MOV CS:[MMAXREP+SI],AX
MOV WORD PTR CS:SELFM5+2,AX
MOV WORD PTR CS:SELFM6+2,AX
MOV SI,CS:[MREPEAT+SI]
JMP MIX_LOOPX
SILENT_FILLX:
MOV ES,AX
CMP CS:[SAM_SEG1],0
JNE MIX_ENDX
SILENT_FILLL:
XOR WORD PTR [DI],8080H
INC DI
INC DI
LOOP SILENT_FILLL
JMP MIX_ENDX
MIX_CHANNELX ENDP
;-----------------------------------------------------------------------------
FIXUP_CHANNELS PROC NEAR
MOV DI,DMA_PTR
MOV DS,DMA_SEG
FIXUP_LOOP:
XOR DWORD PTR [DI],80808080H
INC DI
ROL WORD PTR [DI],8
ADD DI,3
LOOP FIXUP_LOOP
MOV AX,CS
MOV DS,AX
RET
FIXUP_CHANNELS ENDP
;-----------------------------------------------------------------------------
MIX_CHANNELS PROC NEAR
CMP SAM_SEG1,0
JE SHORT GO_BACKS
CALL EMS_SAVE
PUSH EAX
MOV EAX,SAM_OFS1
CALL EMS_PAGING
POP EAX
MOV SI,DI ;GET OFFSET IN SAMPLE (START ALWAYS= 0)
MOV DX,SAM_FRQ1 ;OFF ADD AFTER FIXED POINT (X/256)
MOVZX BP,DH ;OFF ADD BEFORE FIXED POINT
MOV DH,SAM_OFL1 ;ACTUAL AFTER FIXED POINT VALUE
MOV BX,SAM_MAX1 ;COUNTER FOR SAMPLE ENDING
MOV DI,DMA_PTR
MOV DS,DMA_SEG ;DS:DI IS DMA-BUFFER POINTER
PUSH SI
MIX_LOOPS:
MOV AL,ES:[SI]
ADD DH,DL
ADC SI,BP
SUB DH,DL
ADD DH,DL
SBB BX,BP
JC SHORT REST_FILLS
JZ SHORT REST_FILLS
SAR BYTE PTR [DI],1
MOV AH,ES:[SI]
SAR BYTE PTR [DI+1],1
ADD DH,DL
ADC SI,BP
ADD [DI],AX
XOR WORD PTR [DI],8080H
INC DI
INC DI
SUB DH,DL
ADD DH,DL
SBB BX,BP
JC SHORT REST_FILLS
JZ SHORT REST_FILLS
LOOP MIX_LOOPS
MIX_ENDS:
MOV AX,CS
MOV DS,AX
MOV SAM_OFL1,DH
POP AX
SUB SI,AX
PUSH EAX
MOVZX EAX,SI
ADD SAM_OFS1,EAX
POP EAX
MOV SAM_MAX1,BX
CALL EMS_RESTORE
GO_BACKS:
RET
REST_FILLS:
MOV CS:SAM_SEG1,0
SILENT_FILLS:
XOR WORD PTR [DI],8080H
INC DI
INC DI
LOOP SILENT_FILLS
JMP MIX_ENDS
MIX_CHANNELS ENDP
;-----------------------------------------------------------------------------
MIX_CHANNELS1 PROC NEAR
CMP SAM_SEG1,0
JE SHORT GO_BACKS1
CALL EMS_SAVE
PUSH EAX
MOV EAX,SAM_OFS1
CALL EMS_PAGING
POP EAX
MOV SI,DI ;GET OFFSET IN SAMPLE (START ALWAYS= 0)
MOV DX,SAM_FRQ1 ;OFF ADD AFTER FIXED POINT (X/256)
MOVZX BP,DH ;OFF ADD BEFORE FIXED POINT
MOV DH,SAM_OFL1 ;ACTUAL AFTER FIXED POINT VALUE
MOV BX,SAM_MAX1 ;COUNTER FOR SAMPLE ENDING
MOV DI,DMA_PTR
MOV DS,DMA_SEG ;DS:DI IS DMA-BUFFER POINTER
PUSH SI
MIX_LOOPS1:
MOV AL,ES:[SI]
ADD DH,DL
ADC SI,BP
SUB DH,DL
ADD DH,DL
SBB BX,BP
JC SHORT MIX_ENDS1
JZ SHORT MIX_ENDS1
SAR BYTE PTR [DI],1
MOV AH,ES:[SI]
SAR BYTE PTR [DI+1],1
ADD DH,DL
ADC SI,BP
ADD [DI],AX
ADD DI,4
SUB DH,DL
ADD DH,DL
SBB BX,BP
JC SHORT MIX_ENDS1
JZ SHORT MIX_ENDS1
LOOP MIX_LOOPS1
MIX_REST1:
MOV AX,CS
MOV DS,AX
MOV SAM_OFL1,DH
POP AX
SUB SI,AX
PUSH EAX
MOVZX EAX,SI
ADD SAM_OFS1,EAX
POP EAX
MOV SAM_MAX1,BX
CALL EMS_RESTORE
GO_BACKS1:
RET
MIX_ENDS1:
MOV CS:SAM_SEG1,0
JMP MIX_REST1
MIX_CHANNELS1 ENDP
;-----------------------------------------------------------------------------
MIX_CHANNELS2 PROC NEAR
CMP SAM_SEG2,0
JE SHORT GO_BACKS2
CALL EMS_SAVE
PUSH EAX
MOV EAX,SAM_OFS2
CALL EMS_PAGING
POP EAX
MOV SI,DI ;GET OFFSET IN SAMPLE (START ALWAYS= 0)
MOV DX,SAM_FRQ2 ;OFF ADD AFTER FIXED POINT (X/256)
MOVZX BP,DH ;OFF ADD BEFORE FIXED POINT
MOV DH,SAM_OFL2 ;ACTUAL AFTER FIXED POINT VALUE
MOV BX,SAM_MAX2 ;COUNTER FOR SAMPLE ENDING
MOV DI,DMA_PTR
MOV DS,DMA_SEG ;DS:DI IS DMA-BUFFER POINTER
PUSH SI
MIX_LOOPS2:
MOV AL,ES:[SI]
ADD DH,DL
ADC SI,BP
SUB DH,DL
ADD DH,DL
SBB BX,BP
JC SHORT MIX_ENDS2
JZ SHORT MIX_ENDS2
SAR BYTE PTR [DI],1
MOV AH,ES:[SI]
SAR BYTE PTR [DI+1],1
ADD DH,DL
ADC SI,BP
ADD [DI],AX
ADD DI,4
SUB DH,DL
ADD DH,DL
SBB BX,BP
JC SHORT MIX_ENDS2
JZ SHORT MIX_ENDS2
LOOP MIX_LOOPS2
MIX_REST2:
MOV AX,CS
MOV DS,AX
MOV SAM_OFL2,DH
POP AX
SUB SI,AX
PUSH EAX
MOVZX EAX,SI
ADD SAM_OFS2,EAX
POP EAX
MOV SAM_MAX2,BX
CALL EMS_RESTORE
GO_BACKS2:
RET
MIX_ENDS2:
MOV CS:SAM_SEG2,0
JMP MIX_REST2
MIX_CHANNELS2 ENDP
;-----------------------------------------------------------------------------
; SET EMS-PAGES
; IN: EAX= EMS-OFFSET
; OUT: ES:DI= SEGMENT & OFFSET IN EMS PAGE
;-----------------------------------------------------------------------------
EMS_PAGING PROC NEAR
MOV DI,AX
AND DI,3FFFH
MOV ES,EMS_SEG
PUSHA
PUSH EAX
SHR EAX,14
MOV BX,AX
XOR AL,AL
MOV DX,EMS_HANDLE
MOV AH,44H
INT 67H
INC AL
INC BX
MOV AH,44H
INT 67H
INC AL
INC BX
MOV AH,44H
INT 67H
INC AL
INC BX
MOV AH,44H
INT 67H
POP EAX
POPA
RET
EMS_PAGING ENDP
;-----------------------------------------------------------------------------
EMS_SAVE PROC NEAR
PUSH AX DX
MOV DX,EMS_HANDLE ;EMS MAPPING SAVE
MOV AH,47H
INT 67H
POP DX AX
RET
EMS_SAVE ENDP
;-----------------------------------------------------------------------------
EMS_RESTORE PROC NEAR
PUSH AX DX
MOV DX,EMS_HANDLE ;EMS MAPPING RESTORE
MOV AH,48H
INT 67H
POP DX AX
RET
EMS_RESTORE ENDP
;╒═══════════════════════════════════════════════════════════════════════════╕
;│ DESCRIPTION: Calculates the values for the mixing multiplication table. │
;│ This table is used during 4/8-channel mixing to speed up the │
;│ operation by avoiding 'mul' instructions. │
;╘═══════════════════════════════════════════════════════════════════════════╛
MAKE_MIXMUL_VOLUMETABLE PROC NEAR
MOV ES,DMA_SEG
MOV DI,MIXMUL_OFFSET
CLD
MOV CX,CH_NUMB
SHR CL,3
MOV CH,SBPRO_FLAG
XOR BX,BX ; start with volume 0
INC BH
VOLUMELOOP: ; start with sample 0
SAMPLELOOP:
MOV AL,BL
XCHG CL,CH
IMUL BH
OR AH,AH ; ROUND UP
JNS SHORT GMK1
OR AL,AL
JZ SHORT GMK1
INC AH
GMK1: SAL AX,CL
XCHG CL,CH
MOV AL,AH
SAR AL,CL ; 8- CHANNEL MIXING
STOSB
INC BL
JNZ SAMPLELOOP
INC BH
CMP BH,64
JBE VOLUMELOOP
RET
MAKE_MIXMUL_VOLUMETABLE ENDP
;=============================================================================
;=============================================================================
;
; DOS ROUTINES
;
;=============================================================================
;-----------------------------------------------------------------------------
; TIMER-IRQ-HANDLER: 1024 HZ CALLING FREQUENCE
;-----------------------------------------------------------------------------
TIMER_HANDLER PROC FAR
PUSH AX
CMP CS:SYSTEM,0
JNE SHORT WT6
MOV AL,UHR_STATC
OUT UHR_INDEX,AL
JMP SHORT WT1
WT1: JMP SHORT WT2
WT2: IN AL,UHR_PORT
WT6: MOV AL,20H
OUT IRQ3,AL
OUT IRQ1,AL
CMP CS:MOD_STAT,0
JE SHORT WT3
DEC CS:BPM_COUNT ;1024/20= 50 HZ
JNZ SHORT WT3
STI
PUSHA
PUSH DS ES CS
POP DS
MOV AL,BPM_SPEED
MOV BPM_COUNT,AL
CALL CONTROL_CHANNELS
CALL MIXUP_CHANNELS
TEST SB_MODUS,2
JZ SHORT WT5
MOV MOD_STAT,4
JMP SHORT WT4
WT5: CMP MOD_STAT,4
JB SHORT WT4
MOV MOD_STAT,2
WT4: POP ES DS
POPA
WT3: POP AX
CMP CS:SYSTEM,0
JE SHORT WT7
DEC CS:IRQCOUNT
JNZ SHORT WT7
MOV CS:IRQCOUNT,56
JMP DWORD PTR CS:[HOLD70]
WT7: IRET
TIMER_HANDLER ENDP
;-----------------------------------------------------------------------------
; SOUNDBLASTER-IRQ-HANDLER
;-----------------------------------------------------------------------------
SBIRQ_HANDLER PROC FAR
PUSH AX CX DX
MOV DX,CS:SB_RSTAT ;CLEAR IRQ
IN AL,DX
DEC DX
DEC DX
MOV CX,1000H
WUV1: IN AL,DX
OR AL,AL
JNS SHORT WUW1
LOOP WUV1
WUW1: CMP CS:SBPRO_FLAG,1
JE SHORT WUV4
MOV AL,14H ;SB MONO
OUT DX,AL
MOV CX,1000H
WUV2: IN AL,DX ;WAIT UNTIL READY
OR AL,AL
JNS SHORT WUW2
LOOP WUV2
WUW2: MOV AL,0FFH ;SET MAX BLOCK SIZE
OUT DX,AL
MOV CX,1000H
WUV3: IN AL,DX ;WAIT UNTIL READY
OR AL,AL
JNS SHORT WUW3
LOOP WUV3
WUW3: MOV AL,0FFH
OUT DX,AL
MOV AL,20H
OUT IRQ1,AL
POP DX CX AX
IRET
WUV4: MOV AL,48H ;SB STEREO
OUT DX,AL
MOV CX,1000H
WUV5: IN AL,DX ;WAIT UNTIL READY
OR AL,AL
JNS SHORT WUW5
LOOP WUV5
WUW5: MOV AL,0FFH ;SET MAX BLOCK SIZE
OUT DX,AL
MOV CX,1000H
WUV6: IN AL,DX ;WAIT UNTIL READY
OR AL,AL
JNS SHORT WUW6
LOOP WUV6
WUW6: MOV AL,0FFH
OUT DX,AL
MOV CX,1000H
WUV7: IN AL,DX ;WAIT UNTIL READY
OR AL,AL
JNS SHORT WUW7
LOOP WUV7
WUW7: MOV AL,91H ;START SBPRO
OUT DX,AL
MOV AL,20H
OUT IRQ1,AL
POP DX CX AX
IRET
SBIRQ_HANDLER ENDP
;-----------------------------------------------------------------------------
; SERVICE IRQ SWAPPING; USES THE REAL-TIME-CLOCK OR TIMER IRQ
;-----------------------------------------------------------------------------
IRQ_INIT PROC NEAR
CLI
XOR AX,AX
MOV ES,AX
CMP SYSTEM,0
JNE WU11
MOV AX,COMP_SPEED2 ;XCHANGE INIT & STOP DATA FOR DOS
MOV DX,COMP_SPEED3
MOV COMP_SPEED2,DX
MOV COMP_SPEED3,AX
MOV AL,UHR_STATA ;SET CLOCK-IRQ-RATE (1024 HZ)
OUT UHR_INDEX,AL
JMP SHORT WU1
WU1: JMP SHORT WU2
WU2: XCHG AL,AH
IN AL,UHR_PORT
AND AL,0F0H
OR AL,DL
XCHG AL,AH
OUT UHR_INDEX,AL
JMP SHORT WU3
WU3: JMP SHORT WU4
WU4: XCHG AL,AH
OUT UHR_PORT,AL
JMP SHORT WU5
WU5: JMP SHORT WU6
WU6: MOV AL,UHR_STATB ;SET CLOCK-IRQ-MODUS
OUT UHR_INDEX,AL
JMP SHORT WU7
WU7: JMP SHORT WU8
WU8: XCHG AL,AH
IN AL,UHR_PORT
AND AL,08FH
MOV DL,DH
AND DL,40H
OR AL,DL
XCHG AL,AH
OUT UHR_INDEX,AL
JMP SHORT WU9
WU9: JMP SHORT WU10
WU10: XCHG AL,AH
OUT UHR_PORT,AL
IN AL,IRQ4 ;IRQ8 DE-/MASK
AND AL,0FEH
MOV DL,DH
AND DL,1
OR AL,DL
OUT IRQ4,AL
IN AL,IRQ2 ;SBIRQ DE-/MASK
MOV CL,IRQ_NUMBER
SHL DL,CL
MOV DH,0FEH
ROL DH,CL
AND AL,DH
OR AL,DL
OUT IRQ2,AL
MOV BX,4*70H ;CLOCK-IRQ DETOUR
MOV EAX,ES:[BX]
MOV EDX,HOLD70
MOV ES:[BX],EDX
MOV HOLD70,EAX
JMP WU12
WU11: MOV AX,COMP_SPEED4 ;FOR WINDOWS
MOV DX,COMP_SPEED5
MOV COMP_SPEED4,DX
MOV COMP_SPEED5,AX
MOV AL,00110110B ;SET TIMER0 TO 1.024 KHZ
OUT PIT1,AL ;DIVIDER =1193
MOV AL,DL
OUT TIMER0,AL
JMP SHORT OUT40
OUT40: MOV AL,DH
OUT TIMER0,AL
MOV AX,COMP_SPEED2 ;XCHANGE INIT & STOP DATA FOR DOS
MOV DX,COMP_SPEED3
MOV COMP_SPEED2,DX
MOV COMP_SPEED3,AX
IN AL,IRQ2 ;SBIRQ DE-/MASK
MOV CL,IRQ_NUMBER
SHL DL,CL
MOV DH,0FEH
ROL DH,CL
AND AL,DH
OR AL,DL
OUT IRQ2,AL
MOV BX,4*8H ;TIMEOUT-IRQ DETOUR
MOV EAX,ES:[BX]
MOV EDX,HOLD70
MOV ES:[BX],EDX
MOV HOLD70,EAX
WU12: MOV BL,CL ;SOUNDBLASTER-IRQ DETOUR
ADD BL,8
SHL BL,2
XOR BH,BH
MOV EAX,ES:[BX]
MOV EDX,HOLDSB
MOV ES:[BX],EDX
MOV HOLDSB,EAX
MOV AL,20H
OUT IRQ3,AL
OUT IRQ1,AL
STI
RET
IRQ_INIT ENDP
;----------------------------------------------------------------------------
; ALLOCATES MEMORY FOR TRACKS, CARRY-FLAG 0=OK 1=ABBRUCH
;----------------------------------------------------------------------------
SET_TMEM PROC NEAR
MOV AH,48H
MOV BX,1000H ;64K TRACK BUFFER (WILL BE RESIZED)
INT 21H
JC SHORT STCM1
MOV TRACK_SEG,AX
MOV AH,48H
MOV BX,80H ;2K PATTERN BUFFER
INT 21H
JC SHORT STCM1
MOV PATTERN_SEG,AX
STCM1: RET
SET_TMEM ENDP
;----------------------------------------------------------------------------
; DEALLOCATES MEMORY OF TRACKS
;----------------------------------------------------------------------------
FREE_TMEM PROC NEAR
MOV AX,SAMPLE_SEG
MOV ES,AX
MOV AH,49H
INT 21H
MOV AX,TRACK_SEG
MOV ES,AX
MOV AH,49H
INT 21H
RET
FREE_TMEM ENDP
;----------------------------------------------------------------------------
; ALLOCATES MEMORY OF DMA_BUFFER & MIXMUL_TABLE, CARRY 0=OK 1=ERROR
;----------------------------------------------------------------------------
SET_DMEM PROC NEAR
MOV AH,48H
MOV BX,0600H ;24K BUFFER
INT 21H
JC SHORT SDCM1
MOV DMA_SEG,AX
MOV DMA_OFFSET,0 ;DMA_BUFFER CA. 8K
MOV MIXMUL_OFFSET,2000H ;MIXMUL_TAB CA. 16K
AND AX,0FFFH ;CHECK FOR 64K DMA PAGE
CMP AX,0E00H
JB SHORT SDCM2
MOV DMA_OFFSET,4000H
MOV MIXMUL_OFFSET,0
SDCM2: CLC
SDCM1: RET
SET_DMEM ENDP
;----------------------------------------------------------------------------
; DEALLOCATES MEMORY OF DMA & MIXMUL_VOLUMETABLE
;----------------------------------------------------------------------------
FREE_DMEM PROC NEAR
MOV AX,DMA_SEG
MOV ES,AX
MOV AH,49H
INT 21H
RET
FREE_DMEM ENDP
;-----------------------------------------------------------------------------
FREE_MEM PROC NEAR
MOV BX,PROG_END_SEG
SUB BX,PROG_START_SEG
MOV ES,PROG_START_SEG
MOV AH,4AH
INT 21H
RET
FREE_MEM ENDP
;=============================================================================
;=============================================================================
;
; SOUNDBLASTER HARDWARE ROUTINES
;
;=============================================================================
; SET MIXER IF PRESENT
;-----------------------------------------------------------------------------
MIXER_INIT PROC NEAR
MOV DX,SBP_MIXERI
COMMENT *
XOR AL,AL ;RESET MIXER SWITCHED OFF
OUT DX,AL ;BECAUSE IT IS NOT WORKING PROPERLY
MOV CX,100 ;WITH SB16 ASP (GAIN x2,x3,..)
CALL WAIT_TIME
MOV DX,SBP_MIXERD
XOR AL,AL
OUT DX,AL
DEC DX
*
MOV AL,22H
OUT DX,AL
INC DX
MOV AL,255 ;MAX MASTERVOLUME
OUT DX,AL
DEC DX
MOV AL,2
OUT DX,AL
INC DX
MOV AL,255 ;MAX DSPVOLUME
OUT DX,AL
DEC DX
MOV AL,0EH
OUT DX,AL
INC DX
MOV AL,0H ;FILTER & MONO
MOV SBPRO_FLAG,AL
CMP SB_TYP,1 ;SBPRO ENABLED?
JNE SHORT GMIX1
MOV SBPRO_FLAG,1
MOV AL,2H ;FILTER & STEREO
GMIX1: OUT DX,AL
RET
MIXER_INIT ENDP
;-----------------------------------------------------------------------------
; SET DMA READY FOR TRANSFER WITH AUTOINIT
; IN: DX:BX= BLOCKPOINTER, CX= SIZE
;-----------------------------------------------------------------------------
DMA_INIT PROC NEAR
MOV AX,DX ;CONVERT ADDRESS TO PAGE AND OFFSET
SHL DX,4
SHR AH,4 ;PAGE
ADD BX,DX ;OFFSET NEW
ADC AH,0
MOV DMA_PAGE,AH
MOV DMA_POFF,BX
DEC CX ;DMA SIZE= BLOCK SIZE -1
MOV AL,DMA_CHANNEL
OR AL,4
OUT 0AH,AL ;DMA-CHANNEL MASK
AND AL,3
OR AL,01011000B
OUT 0BH,AL ;DMA-MODUS FOR SB
OUT 0CH,AL ;FLIP-FLOP CLEAR
AND AL,3
MOVZX DX,AL
SHL DL,1
MOV AL,BL ;DMA CHANNEL 1-4
OUT DX,AL ;OFFSET LOW
MOV AL,BH
OUT DX,AL ;OFFSET HIGH
INC DL
MOV AL,CL
OUT DX,AL ;SIZE LOW
MOV AL,CH
OUT DX,AL ;SIZE HIGH
MOV BX,DX
SHR BL,1
MOV DL,[DMA_DATA+BX]
MOV AL,AH
OUT DX,AL ;PAGE
MOV AL,BL
OUT 0AH,AL ;DMA-CHANNEL DEMASK
RET
DMA_INIT ENDP
;-----------------------------------------------------------------------------
; DSP SET SAMPLE RATE
; IN: AH= RATE (= 256-(1000000/FREQ))
;-----------------------------------------------------------------------------
DSP_RATE PROC NEAR
PUSH AX
MOV AH,40H
CALL DSP_WRITE
POP AX
CALL DSP_WRITE
RET
DSP_RATE ENDP
;-----------------------------------------------------------------------------
; DSP OUT BLOCK
;-----------------------------------------------------------------------------
DSP_OUT PROC NEAR
CMP SBPRO_FLAG,1
JE SHORT GDSP1
MOV AH,14H ;MONO DMA-OUTPUT NEW
CALL DSP_WRITE
MOV AH,0FFH ;SIZE LOW
CALL DSP_WRITE
MOV AH,0FFH ;SIZE HIGH
CALL DSP_WRITE
RET
GDSP1: MOV AH,48H ;STEREO DMA-OUTPUT NEW
CALL DSP_WRITE
MOV AH,0FFH ;SIZE LOW
CALL DSP_WRITE
MOV AH,0FFH ;SIZE HIGH
CALL DSP_WRITE
MOV AH,91H ;START
CALL DSP_WRITE
RET
DSP_OUT ENDP
;-----------------------------------------------------------------------------
; DSP RESET
; OUT: CARRY 1= FAILURE
;-----------------------------------------------------------------------------
DSP_RESET PROC NEAR
CALL DSP_OFF
OUT 0DH,AL ;DMA MASTER CLEAR
CALL MIXER_INIT
MOV DX,SB_RESET
MOV AL,1
OUT DX,AL ;SEND RESET COMMAND
MOV CX,4
CALL WAIT_TIME ;WAIT
XOR AL,AL
OUT DX,AL ;CLEAR RESET COMMAND
MOV CX,120
CALL WAIT_TIME ;WAIT
MOV DX,SB_RSTAT
IN AL,DX ;CHECK IF SUCCESSFUL
OR AL,AL
JNS SHORT GDRES1
MOV DX,SB_READ
IN AL,DX
CMP AL,0AAH
JNE SHORT GDRES1
CALL DSP_ON
CLC ;RESET OK
RET
GDRES1: STC ;RESET FAILURE
RET
DSP_RESET ENDP
;-----------------------------------------------------------------------------
; SPEAKER ON
;-----------------------------------------------------------------------------
DSP_ON PROC NEAR
MOV AH,0D1H ;SPEAKER ON
CALL DSP_WRITE
MOV CX,120
CALL WAIT_TIME ;WAIT
RET
DSP_ON ENDP
;-----------------------------------------------------------------------------
; SPEAKER OFF
;-----------------------------------------------------------------------------
DSP_OFF PROC NEAR
PUSH CS
POP DS
CMP SB_TYP,1
JE SHORT GDSPF1 ;SB
MOV AL,DMA_CHANNEL ;DMA DEMASK
OUT 0AH,AL
MOV AH,0D0H ;DMA STOP
CALL DSP_WRITE
MOV AH,0D3H ;DSP OFF
CALL DSP_WRITE
MOV CX,240
CALL WAIT_TIME ;WAIT
RET
GDSPF1: MOV DX,SBP_MIXERI ;SBPRO
MOV AL,22H
OUT DX,AL
INC DX
MOV AL,0 ;MIN MASTERVOLUME
OUT DX,AL
DEC DX
MOV AL,2
OUT DX,AL
INC DX
MOV AL,0 ;MIN DSPVOLUME
OUT DX,AL
MOV CX,240
CALL WAIT_TIME
RET
DSP_OFF ENDP
;-----------------------------------------------------------------------------
; READ BYTE
; OUT: AL= BYTE
;-----------------------------------------------------------------------------
DSP_READ PROC NEAR
MOV DX,SB_READ
MOV CX,-1 ;FAIL SAFE
LDRD1: IN AL,DX
CMP AL,0AAH
JNE SHORT LDRD2
LOOP LDRD1
LDRD2: RET
DSP_READ ENDP
;-----------------------------------------------------------------------------
; WRITE BYTE
; IN: AH= BYTE
;-----------------------------------------------------------------------------
DSP_WRITE PROC NEAR
MOV DX,SB_WRITE
MOV CX,1000H ;FAIL SAFE
LDWR1: IN AL,DX
OR AL,AL
JNS SHORT LDWR2
LOOP LDWR1
LDWR2: MOV AL,AH
OUT DX,AL
RET
DSP_WRITE ENDP
;-----------------------------------------------------------------------------
; WAIT TIME
; CX = XXX MILLISECONDS
;-----------------------------------------------------------------------------
WAIT_TIME PROC NEAR
PUSH AX BX EDX
LWT1: IN AL,TIMER0
MOV BL,AL
IN AL,TIMER0
MOV BH,AL
MOV EDX,500000 ;FAIL SAFE
LWT2: IN AL,TIMER0 ;1 MILLISEC WAIT
XCHG AL,AH
IN AL,TIMER0
XCHG AL,AH
SUB AX,BX
NEG AX
DEC EDX
JZ SHORT LWT3
CMP AX,1193
JB LWT2
LWT3: LOOP LWT1
POP EDX BX AX
RET
WAIT_TIME ENDP
;=============================================================================
;=============================================================================
;
; MOD LOAD ROUTINES
;
;=============================================================================
; LOAD A MODFILE
; DS:DX= FILENAME
;-----------------------------------------------------------------------------
LOAD_MOD PROC NEAR
MOV AX,3D00H ;OPEN MOD
INT 21H
JC GLDM1
PUSH CS
POP DS
MOV GDDHANDLE,AX
GLDM0: CALL SET_DMEM ;ALLOCATE DMA-BUFFER
CALL SET_TMEM ;ALLOCATE TRACK-BUFFER
MOV BX,GDDHANDLE
MOV GDDZEIG,0
MOV CX,10 ;GET SONG-NAME
LLDM1: CALL GET_QUEUE
JC GLDM2
LOOP LLDM1
MOV SAMPLE_SIZE,0
XOR SI,SI ;31 INSTRUMENTS
LLDM2: MOV AX,SAMPLE_SIZE
MOV [SAMPLE_SEG+SI],AX
MOV CX,11 ;INSTRUMENT NAME
LLDM3: CALL GET_QUEUE
JC GLDM2
LOOP LLDM3
XOR EAX,EAX
CALL GET_QUEUE ;INSTRUMENT SIZE
XCHG AL,AH
CMP AX,2
JAE SHORT GLDM20
XOR AX,AX
GLDM20: SHL AX,1
MOV [ISIZE+SI],AX ;NO MORE THAN 64K
OR EAX,EAX
JZ SHORT GLDM9
SHR EAX,4
INC AX
ADD SAMPLE_SIZE,AX
GLDM9: CALL GET_QUEUE ;IVOLUME & FINETUNE
XCHG AL,AH
MOV [IVOL_FINETUNE+SI],AX
CALL GET_QUEUE ;ILOOP_START
XCHG AL,AH
SHL AX,1
MOV [ILOOP_START+SI],AX
CALL GET_QUEUE ;ILOOP_SIZE
XCHG AL,AH
SHL AX,1
MOV [ILOOP_SIZE+SI],AX
INC SI
INC SI
CMP SI,62
JB LLDM2
CALL GET_QUEUE ;SONG_SIZE & LOOP_BYTE OR NOTHING
JC GLDM2
MOV SONG_SIZE,AL
MOV SONG_LOOP,AH
XOR DX,DX
MOV CX,64 ;SONG_DATA
MOV DI,OFFSET SONG_DATA
LLDM4: CALL GET_QUEUE
JC GLDM2
MOV [DI],AX
CMP AL,DL ;FIND HIGHEST PATTERN NUMBER
JBE SHORT GLDM12
MOV DL,AL
GLDM12: CMP AH,DL
JBE SHORT GLDM13
MOV DL,AH
GLDM13: INC DI
INC DI
LOOP LLDM4
INC DL
CLD
MOV PATTERN_NUMBER,DX
CALL GET_QUEUE
PUSH AX
CALL GET_QUEUE
SHL EAX,16
POP AX
MOV MOD_SIGN,EAX
CMP EAX,"NHC8" ;8CHN-MOD
JE SHORT GLDM3
CMP EAX,"8TLF" ;FLT8-MOD
JE SHORT GLDM3
CMP EAX,".K.M" ;M.K.-MOD
JE SHORT GLDM10
CMP EAX,"4TLF" ;FLT4-MOD
JNE GLDM2
GLDM10: MOV CH_NUMB,4 ;4-CHANNEL-MOD
MOV PATTERN_SIZE,1024
MOV AH,0
MOV TRACK_INFO+4,AH
MOV TRACK_INFO+5,AH
MOV TRACK_INFO+6,AH
MOV TRACK_INFO+7,AH
JMP SHORT GLDM11
GLDM3: MOV CH_NUMB,8 ;8-CHANNEL-MOD: GET-PATTERNS
MOV PATTERN_SIZE,2048
GLDM11: MOV PATTERN_CURRENT,0
MOV TRACK_NUMBER,0
LLDM7: MOV ES,PATTERN_SEG
MOV BX,GDDHANDLE
XOR DI,DI
LLDM6: XOR SI,SI
LLDM5: CALL GET_QUEUE ;GET PATTERNS AND SPLIT INTO TRACKS
JC GLDM2
MOV DX,AX
CALL GET_QUEUE
JC GLDM2
ROL AL,4 ;MODIFY PATTERN DATA
MOV CL,AL ;LOOK AT PROTRACKER-CODE HEADER
AND AL,0FH
AND CL,0F0H
MOV CH,DL
AND DL,0FH
AND CH,0F0H
OR AL,CH
OR DL,CL
XCHG DL,DH
PUSH DI
ADD DI,SI
SHL EAX,16
MOV AX,DX
STOSD
POP DI
ADD SI,256
CMP SI,PATTERN_SIZE ;NEXT TRACK/CHANNEL
JB LLDM5
ADD DI,4 ;NEXT NOTE
CMP DI,256
JB LLDM6
CALL SET_FREQUENCIES
MOV ES,TRACK_SEG ;INTEGRATE TRACK1-8
MOV DX,TRACK_NUMBER
XCHG DL,DH
MOV DS,PATTERN_SEG
XOR SI,SI
XOR BX,BX
LLDM8: XOR DI,DI
LLDM9: CMP DX,DI
JBE SHORT GLDM4
PUSH SI DI
MOV CX,256/4
REPE CMPSD ;TRACK ALREADY EXISTS?
POP DI SI
PUSHF
ADD DI,256
POPF
JNE LLDM9
JMP SHORT GLDM5
GLDM4: INC DH ;NO, ADD TRACK TO BUFFER
PUSH SI
MOV CX,256/4
REP MOVSD
POP SI
GLDM5: MOV AX,DI ;YES
DEC AH
MOV CS:[TRACK_INFO+BX],AH
INC BX
ADD SI,256
CMP BX,CS:CH_NUMB
JB LLDM8
PUSH CS
POP DS
XCHG DL,DH
MOV TRACK_NUMBER,DX
XOR BX,BX ;MAKE TRACKLISTE
MOV AX,PATTERN_CURRENT
LLDM10: CMP [SONG_DATA+BX],AL
JNE SHORT GLDM6
MOV AH,TRACK_INFO
MOV [TRACK1_DATA+BX],AH
MOV AH,TRACK_INFO+1
MOV [TRACK2_DATA+BX],AH
MOV AH,TRACK_INFO+2
MOV [TRACK3_DATA+BX],AH
MOV AH,TRACK_INFO+3
MOV [TRACK4_DATA+BX],AH
MOV AH,TRACK_INFO+4
MOV [TRACK5_DATA+BX],AH
MOV AH,TRACK_INFO+5
MOV [TRACK6_DATA+BX],AH
MOV AH,TRACK_INFO+6
MOV [TRACK7_DATA+BX],AH
MOV AH,TRACK_INFO+7
MOV [TRACK8_DATA+BX],AH
GLDM6: INC BX
CMP BL,128
JB LLDM10
INC PATTERN_CURRENT ;GET NEXT PATTERN
MOV AX,PATTERN_CURRENT
CMP AX,PATTERN_NUMBER
JB LLDM7
GLDM7: MOV ES,PATTERN_SEG ;CLEAR PATTERN SEGMENT
MOV AH,49H
INT 21H
MOV BX,TRACK_NUMBER ;TRACK SEGMENT RESIZE
SHL BX,4
MOV ES,TRACK_SEG
MOV AH,4AH
INT 21H
MOV BX,SAMPLE_SIZE ;SAMPLE SEGMENT ALLOCATE
MOV AH,48H
INT 21H
XOR BX,BX
LLDM11: ADD [SAMPLE_SEG+BX],AX
INC BX
INC BX
CMP BX,62
JB LLDM11
MOV BX,GDDHANDLE
XOR SI,SI ;LOAD SAMPLES
LLDM12: MOV CX,[ISIZE+SI]
MOV ES,[SAMPLE_SEG+SI]
XOR DI,DI
SHR CX,1
JCXZ SHORT GLDM8
LLDM13: CALL GET_QUEUE
STOSW
LOOP LLDM13
GLDM8: INC SI
INC SI
CMP SI,62
JB LLDM12
CLC
JMP SHORT GLDM15
GLDM2: STC
GLDM15: PUSHF
MOV AH,3EH
INT 21H
POPF
GLDM1: RET
LOAD_MOD ENDP
;-----------------------------------------------------------------------------
; REPLACE MOST PERIODS WITH INDEX INTO PERIOD TABLE
;-----------------------------------------------------------------------------
SET_FREQUENCIES PROC NEAR
XOR DI,DI
SET_FREQ1:
MOV AX,ES:[DI] ;get fx
MOV DX,AX
SHR AH,4
CMP AH,3
JE SHORT SET_FREQ4
CMP AH,5
JE SHORT SET_FREQ4
MOV AX,DX
AND AX,0FFFH ; Mask out unwanted bits.
JZ SHORT SET_FREQ4
XOR BX,BX
MOV CX,48 ; 36 periods to cycle through.
SET_FREQ2:
CMP AX,[MT_PERIODTABLE+BX] ; Check the note against the period.
JAE SHORT SET_FREQ3 ; We found it!
INC BX ; Otherwise, update the pointer and
INC BX
LOOP SET_FREQ2 ; keep looping.
SET_FREQ3:
MOV AX,DX ;recall the Note Value
AND AX,0F000H ;just keep fx
INC BX
OR AX,BX ;put in the new offset
STOSW ;store it
DEC DI
DEC DI
SET_FREQ4:
ADD DI,4 ;and go to next channel
CMP DI,PATTERN_SIZE
JB SET_FREQ1
RET
SET_FREQUENCIES ENDP
;-----------------------------------------------------------------------------
; GET WORD FROM BUFFER
; IN: AX= WORD, BX= HANDLE
;-----------------------------------------------------------------------------
GET_QUEUE PROC NEAR
CMP GDDZEIG,0
JNE SHORT GTQ1
PUSH CX DX
MOV DX,OFFSET QUEUE_BUFFER
MOV CX,256
MOV AH,3FH
INT 21H
MOV GDDREST,AX
POP DX CX
GTQ1: PUSH BX
MOV BL,GDDZEIG
XOR BH,BH
CMP BX,GDDREST
JAE SHORT GTQ2
MOV AX,[QUEUE_BUFFER+BX]
ADD GDDZEIG,2
POP BX
CLC
RET
GTQ2: POP BX
STC
RET
GET_QUEUE ENDP
;=============================================================================
; LOAD A SAMPLE
; INPUT: DS:DX= FILENAME, CL= 00H AMIGA-SAMPLE, 80H PC-SAMPLE
; OUTPUT: AX= SAMPLE-HANDLE FOR PLAYING
;-----------------------------------------------------------------------------
LOAD_SAMPLE PROC NEAR
MOV AX,3D00H ;OPEN SAMPLE
INT 21H
JC GSMP1
PUSH CS
POP DS
MOV GDDHANDLE,AX
MOV BX,AX
PUSH CX
CALL GET_FILESIZE
POP CX
GSMP0: AND CL,80H
MOV SAM_FLAG,CL
MOV GDDZEIG,0
MOV CX,AX
AND CX,0FFFEH
SHR AX,4
INC AX
MOV BX,AX
CMP EMS_SEG,0
JNE SHORT GSMP3
PUSHA
XOR SI,SI
XOR AX,AX ;CHECK FOR EMS-MANAGER
MOV ES,AX
MOV EMS_HANDLE,AX
MOV AX,ES:[4*67H+2]
MOV ES,AX
CMP ES:[10],"XMME"
JNE GSMP2
CMP ES:[14],"0XXX"
JNE GSMP2
MOV AH,42H
INT 67H
OR AH,AH
JNZ GSMP2
CMP BX,34
JB GSMP2
MOV AH,41H
INT 67H
MOV EMS_SEG,BX
MOV AH,43H ;ALLOCATE 544K EMS FOR FX
MOV BX,34
INT 67H
MOV EMS_HANDLE,DX
XOR AX,AX
MOV EMS_OFFSET,AX
MOV SAMPLE_ZEIG,AX
POPA
GSMP3: MOV AX,EMS_OFFSET
MOV SI,SAMPLE_ZEIG
MOV [SAMPLE_MEM+SI],AX
MOV [SAMPLE_BIG+SI],CX
ADD SAMPLE_ZEIG,2
MOV DI,CX
SHR DI,4
INC DI
ADD EMS_OFFSET,DI
XOR DI,DI
CLD
INC CX
SHR CX,1
MOV DL,SAM_FLAG
MOV DH,DL
MOVZX BP,FX_VOL
INC BP
AND EAX,0FFFFH
SHL EAX,4
LSMP1: PUSH EAX
OR DI,DI
JNZ SHORT GSMP4
CALL EMS_PAGING
GSMP4: MOV BX,GDDHANDLE
CALL GET_QUEUE
XOR AX,DX
SAR AH,1
SAR AL,1
PUSH DX
MOV BL,AH
CBW
IMUL BP
MOV AL,BL
MOV BL,AH
CBW
IMUL BP
MOV AL,BL
POP DX
STOSW
POP EAX
INC EAX
INC EAX
LOOP LSMP1
CLC
GSMP2: PUSHF
MOV BX,GDDHANDLE
MOV AH,3EH
INT 21H
MOV AX,SI
POPF
GSMP1: RET
LOAD_SAMPLE ENDP
;-----------------------------------------------------------------------------
GET_FILESIZE PROC NEAR
MOV AX,4202H
XOR CX,CX
XOR DX,DX
INT 21H
PUSH AX
MOV AX,4200H
XOR CX,CX
XOR DX,DX
INT 21H
POP AX
RET
GET_FILESIZE ENDP
;=============================================================================
; LOAD CONFIG DATA (PARSER)
;-----------------------------------------------------------------------------
CONFIG_INIT PROC NEAR
MOV AX,CS ;SAVE THE SEGMENT OF CODE
MOV DS,AX
MOV WORD PTR HOLD70+2,AX ;THIS LINE IS IMPORTANT
MOV WORD PTR HOLDSB+2,AX ;THIS LINE IS IMPORTANT
MOV DX,OFFSET CONFIG_NAME
MOV AH,3DH
MOV AL,80H
INT 21H
JC GCFG1
MOV GDDHANDLE,AX
LCFG1: XOR SI,SI
MOV DX,OFFSET QUEUE_BUFFER
LCFG2: MOV AH,3FH
MOV BX,GDDHANDLE
MOV CX,1
INT 21H
JC GCFG2
OR AX,AX
JZ GCFG2
MOV DI,DX
CMP BYTE PTR [DI],0AH
JE SHORT GCFG6
INC DX
INC SI
CMP SI,256
JB LCFG2
GCFG6: MOV BX,OFFSET CFG_TEXT1
CALL SEEK_LINE
JNC SHORT GCFG3
LODSB
AND AL,3
MOV SB_TYP,AL
JMP LCFG1
GCFG3: MOV BX,OFFSET CFG_TEXT2
CALL SEEK_LINE
JNC SHORT GCFG4
LODSB
AND AL,3
MOV SB_MODUS,AL
JMP LCFG1
GCFG4: MOV BX,OFFSET CFG_TEXT3
CALL SEEK_LINE
JNC SHORT GCFG5
INC SI
LODSB
DEC AL
AND AL,7
INC AL
SHL AL,4
MOV AH,2
ADD AL,4
MOV SBP_MIXERI,AX
INC AX
MOV SBP_MIXERD,AX
INC AX
MOV SB_RESET,AX
ADD AX,4
MOV SB_READ,AX
ADD AX,2
MOV SB_WRITE,AX
ADD AX,2
MOV SB_RSTAT,AX
JMP LCFG1
GCFG5: MOV BX,OFFSET CFG_TEXT4
CALL SEEK_LINE
JNC SHORT GCFG7
LODSW
AND AL,7
AND AH,7
MOV BH,AH
MOV BL,10
MUL BL
ADD AL,BH
MOV IRQ_NUMBER,AL
JMP LCFG1
GCFG7: MOV BX,OFFSET CFG_TEXT5
CALL SEEK_LINE
JNC SHORT GCFG8
LODSB
AND AL,3
MOV DMA_CHANNEL,AL
JMP LCFG1
GCFG8: MOV BX,OFFSET CFG_TEXT6
CALL SEEK_LINE
JNC SHORT GCFG9
XOR BX,BX
MOV CX,5
LCFG3: MOV AX,BX
MOV DX,10
MUL DX
MOV BX,AX
LODSB
AND AL,0FH
XOR AH,AH
ADD BX,AX
LOOP LCFG3
MOV SAMPLE_RATE,BX
JMP LCFG1
GCFG9: MOV BX,OFFSET CFG_TEXT7
CALL SEEK_LINE
JNC LCFG1
LODSB
AND AL,1
MOV SYSTEM,AL
JMP LCFG1
GCFG2: MOV BX,GDDHANDLE
MOV AH,3EH
INT 21H
GCFG1: RET
CONFIG_INIT ENDP
;-----------------------------------------------------------------------------
SEEK_LINE PROC NEAR
CLD
MOV SI,OFFSET QUEUE_BUFFER
LSKL1: PUSH BX
LSKL2: CMP BYTE PTR [BX],0
JE SHORT GSKL1
CMP SI,DX
JAE SHORT GSKL2
LODSB
CMP AL,[BX]
JNE SHORT GSKL3
INC BX
JMP LSKL2
GSKL3: POP BX
JMP LSKL1
GSKL2: POP BX
CLC
RET
GSKL1: POP BX
STC
RET
SEEK_LINE ENDP
;=============================================================================
;=============================================================================
PLAY_MUSIC PROC NEAR
PUSH CS
POP DS
MOV MOD_STAT,0 ;DEACTIVATE IRQ-HANDLING
CALL DSP_RESET
JC GPM1
CALL MAKE_MIXMUL_VOLUMETABLE
CALL IRQ_INIT
XOR EDX,EDX
MOV EAX,1000000
MOVZX EBX,SAMPLE_RATE
MOV CL,SBPRO_FLAG
DIV EBX
SHR AL,CL
NEG AL
MOV DMA_RATE,AL
XOR EDX,EDX
MOV EAX,369E9400H ;1,193,180 * 300H FOR FREQ CALCS
DIV EBX
MOV MAINFREQ,EAX
MOV AX,BX
MOV BX,20 ;AMIGA TIMING IS 50HZ (SCREEN-REFRESH)
MUL BX
SHRD AX,DX,10
SHL AX,CL
MOV DMA_CX,AX ;# OF BYTE/CYCLE (434 FOR 22222 HZ)
SHL AX,2 ;4 TIMES FORWARD
MOV DMA_MORE,AX
SHL AX,2 ;16 TIMES MAXIMUM (6944 FOR 22222 HZ)
SHR AX,CL
SUB AH,CL
SUB AH,CL
MOV DMA_MAX,AX
MOV AX,DMA_OFFSET
MOV DMA_PTR,AX
MOV DMA_NEWPTR,AX
MOV BPM_SPEED,20 ;CLOCK-IRQ IS 1024HZ DIV 20 = 50HZ
MOV BPM_COUNT,1
MOV MT_SPEED,6 ;DEFAULT PROTRACKER SPEED
XOR AX,AX
MOV MT_COUNTER,AL ;RESET PROTRACKER VARIABLES
MOV MT_PATTERNPOS,AX
MOV MT_SONGPOS,AL
MOV MT_PATTDELAYTIME2,AL
MOV MT_PATTDELAYTIME,AL
MOV MT_PBREAKFLAG,AL
MOV MT_PBREAKPOS,AL
MOV MT_POSJUMPFLAG,AL
MOV MT_LOWMASK,0FFH
MOV CX,43*8/2
CLD
PUSH DS
POP ES
MOV DI,OFFSET NOTE
REP STOSW
INC AL
MOV CX,MAX_CHAN_NUMB
MOV DI,OFFSET TONEPORTDIREC
REP STOSB
MOV BYTE PTR SELFN1+2,2
MOV BYTE PTR SELFN2+2,2
TEST SBPRO_FLAG,1
JZ SHORT GPM2
MOV BYTE PTR SELFN1+2,4
MOV BYTE PTR SELFN2+2,4
GPM2: MOV CX,3 ;PRECALCULATE SAMPLE DATA FOR INIT
LPM1: PUSH CX
CALL CONTROL_CHANNELS
CALL MIXUP_CHANNELS
POP CX
LOOP LPM1
MOV AH,DMA_RATE ;SET SB-SAMPLERATE
CALL DSP_RATE
MOV DX,DMA_SEG ;INITIALIZE DMA
MOV BX,DMA_OFFSET
MOV CX,DMA_MAX
CALL DMA_INIT
MOV MOD_STAT,1 ;ACTIVATE IRQ-HANDLING
CLC
GPM1: RET
PLAY_MUSIC ENDP
;-----------------------------------------------------------------------------
STOP_MUSIC PROC NEAR
PUSH CS
POP DS
MOV MOD_STAT,0
CALL DSP_RESET
CALL IRQ_INIT
MOV IRQCOUNT,56
RET
STOP_MUSIC ENDP
;-----------------------------------------------------------------------------
END_MUSIC PROC NEAR
PUSH CS
POP DS
CALL FREE_TMEM
CALL FREE_DMEM
RET
END_MUSIC ENDP
;-----------------------------------------------------------------------------
GET_SONGMOD PROC NEAR
MOV AL,CS:SB_MODUS
RET
GET_SONGMOD ENDP
;-----------------------------------------------------------------------------
SET_SONGMOD PROC NEAR
AND AL,3
MOV CS:SB_MODUS,AL
RET
SET_SONGMOD ENDP
;-----------------------------------------------------------------------------
GET_SONGPOSITION PROC NEAR
MOV AL,CS:MT_SONGPOS
RET
GET_SONGPOSITION ENDP
;-----------------------------------------------------------------------------
SET_SONGPOSITION PROC NEAR
AND AL,7FH
MOV CS:MT_SONGPOS,AL
MOV CS:MT_PATTERNPOS,0
RET
SET_SONGPOSITION ENDP
;-----------------------------------------------------------------------------
SET_SONGLOOP PROC NEAR
MOV CS:SONG_LOOP,AL
RET
SET_SONGLOOP ENDP
;-----------------------------------------------------------------------------
GET_VOLUME PROC NEAR
MOV AL,CS:MASTER_VOLUME ;0= MIN VOLUME, 255= MAX VOLUME
MOV BL,CS:MUSIC_VOLUME ;0= MIN VOLUME, 255= MAX VOLUME
MOV BH,CS:FX_VOLUME ;0= MIN VOLUME, 255= MAX VOLUME
RET
GET_VOLUME ENDP
;-----------------------------------------------------------------------------
SET_VOLUME PROC NEAR
MOV CS:MASTER_VOLUME,AL ;0= MIN VOLUME, 255= MAX VOLUME
MOV CS:MUSIC_VOLUME,BL ;0= MIN VOLUME, 255= MAX VOLUME
MOV CS:FX_VOLUME,BH ;0= MIN VOLUME, 255= MAX VOLUME
PUSH AX DX
MOVZX DX,AL
INC DX
PUSH DX
MOVZX AX,BL
MUL DX
MOV CS:MUSIC_VOL,AH
POP DX
MOVZX AX,BH
MUL DX
MOV CS:FX_VOL,AH
POP DX AX
RET
SET_VOLUME ENDP
;-----------------------------------------------------------------------------
SET_SAMPLERATE PROC NEAR
CMP AX,10000
JAE SHORT GSSR1
MOV AX,10000
GSSR1: CMP AX,22222
JBE SHORT GSSR2
MOV AX,22222
GSSR2: MOV CS:SAMPLE_RATE,AX ;BEST BETWEEN 10000 - 22222 Hz
RET
SET_SAMPLERATE ENDP
;-----------------------------------------------------------------------------
; START FX SAMPLE
; BX= SAMPLE-HANDLE, CX= SAMPLERATE
;-----------------------------------------------------------------------------
PLAY_SAMPLE PROC NEAR
PUSH CS
POP DS
TEST SB_MODUS,1
JNZ GPSMP1
CMP EMS_SEG,0
JE GPSMP1
MOVZX EAX,CX
SHL EAX,8
MOVZX ECX,SAMPLE_RATE
XOR EDX,EDX
DIV ECX
OR AX,AX
JZ SHORT GPSMP1
CMP SBPRO_FLAG,1
JNE SHORT GPSMP2
XOR SAM_FLAG,1
TEST SAM_FLAG,1
JZ SHORT GPSMP3
CMP SAM_SEG1,0
JE SHORT GPSMP2
CMP SAM_SEG2,0
JNE SHORT GPSMP2
GPSMP4: CLI ;RIGHT CHANNEL SAMPLE
MOV SAM_FRQ2,AX
MOV AX,EMS_SEG
MOV SAM_SEG2,AX
MOVZX EAX,[SAMPLE_MEM+BX]
SHL EAX,4
MOV SAM_OFS2,EAX
MOV AX,[SAMPLE_BIG+BX]
MOV SAM_MAX2,AX
MOV SAM_OFL2,0
STI
RET
GPSMP3: CMP SAM_SEG2,0
JE GPSMP4
CMP SAM_SEG1,0
JNE GPSMP4
GPSMP2: CLI ;LEFT CHANNEL SAMPLE
MOV SAM_FRQ1,AX
MOV AX,EMS_SEG
MOV SAM_SEG1,AX
MOVZX EAX,[SAMPLE_MEM+BX]
SHL EAX,4
MOV SAM_OFS1,EAX
MOV AX,[SAMPLE_BIG+BX]
MOV SAM_MAX1,AX
MOV SAM_OFL1,0
STI
GPSMP1: RET
PLAY_SAMPLE ENDP
;-----------------------------------------------------------------------------
END_SAMPLE PROC NEAR
PUSH CS
POP DS
MOV SAM_SEG1,0
MOV SAM_SEG2,0
CMP EMS_SEG,0
JE SHORT GEMP1
MOV AH,45H
MOV DX,EMS_HANDLE
INT 67H
MOV EMS_SEG,0
GEMP1: RET
END_SAMPLE ENDP
;=============================================================================
;=============================================================================
;
; MAIN ROUTINE
;
;=============================================================================
MAIN_CONT PROC NEAR
MOV AX,CS ;DS SET
MOV DS,AX
MOV PROG_START_SEG,ES ;PROGRAMM START
MOV AX,SS ;AND -END SET
MOV BX,OFFSET PROG_END
ADD BX,STACKLENGTH
MOV SP,BX
SHR BX,4
INC BX
ADD AX,BX
MOV PROG_END_SEG,AX
XOR BX,BX
GL1: MOV AL,ES:[82H+BX]
OR AL,AL
JZ SHORT GL2
CMP AL,20H
JE SHORT GL2
CMP AL,0DH
JE SHORT GL2
MOV [MOD_NAME+BX],AL
INC BX
CMP BX,40H
JB GL1
GL2: OR BX,BX
JZ GMEND
XOR AL,AL
MOV [MOD_NAME+BX],0
CALL FREE_MEM
;-----------------------------------------------------------------------------
; INITIALIZING & STARTING
; MOV AX,22222 ;SET MAXIMUM SAMPLERATE
; CALL SET_SAMPLERATE
; MOV AL,255 ;SET MAXIMUM VOLUME
; MOV BL,AL
; MOV BH,AL
; CALL SET_VOLUME
CALL CONFIG_INIT ;GET CONFIG
MOV DX,OFFSET MOD_NAME ;FIRST, TRY LOADING
CALL LOAD_MOD
JNC SHORT GM1
MOV AH,9
MOV DX,OFFSET ERROR1_TEXT
INT 21H
JMP SHORT GM4
GM1: MOV DX,OFFSET SAMPLE_NAME ;GET SAMPLE
MOV CL,80H ;PC-SAMPLE
CALL LOAD_SAMPLE
JNC SHORT GM2
MOV AH,9
MOV DX,OFFSET ERROR2_TEXT
INT 21H
GM2: CALL PLAY_MUSIC ;TRY STARTING
JNC SHORT GM3
MOV AH,9
MOV DX,OFFSET ERROR3_TEXT
INT 21H
JMP SHORT GM4
;-----------------------------------------------------------------------------
; THIS COULD BE THE MAIN ROUTINE
GM3: MOV AH,9
MOV DX,OFFSET OUT_TEXT
INT 21H
LM1: XOR AH,AH
INT 16H
CMP AL,27
JE SHORT GM5
CMP AL,"+"
JNE SHORT G1
CALL GET_VOLUME
INC AL
CALL SET_VOLUME
JMP LM1
G1: CMP AL,"-"
JNE SHORT G2
CALL GET_VOLUME
DEC AL
CALL SET_VOLUME
JMP LM1
G2: CMP AL,"9"
JNE SHORT G3
MOV AL,0
CALL SET_SONGLOOP
JMP LM1
G3: CMP AL,"0"
JNE SHORT G4
INC SB_MODUS
AND SB_MODUS,3
JMP LM1
G4: CMP AL,"1"
JNE LM1
XOR BX,BX ;HANDLE
MOV CX,22222 ;FREQUENCE
CALL PLAY_SAMPLE
JMP LM1
;-----------------------------------------------------------------------------
; CUTTING OFF
GM5: CALL STOP_MUSIC ;STOP MUSIC
GM4: CALL END_SAMPLE ;UNLOAD SAMPLES
CALL END_MUSIC ;UNLOAD MUSIC
GMEND: MOV AX,4C00H ;DOS
INT 21H
MAIN_CONT ENDP
;=============================================================================
END MAIN